2020-01-18 05:38:21 -03:00
/*
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
*
2021-04-22 05:24:48 -03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 05:38:21 -03:00
*/
2021-02-27 04:50:02 -03:00
# include "Editor.h"
2020-08-10 17:28:38 -03:00
# include "HackStudio.h"
2020-09-19 11:25:05 -03:00
# include "HackStudioWidget.h"
2019-10-21 13:46:55 -03:00
# include "Project.h"
2020-01-15 23:42:54 -03:00
# include <AK/StringBuilder.h>
2021-10-06 11:53:43 -03:00
# include <LibConfig/Client.h>
2020-08-12 18:23:45 -03:00
# include <LibCore/ArgsParser.h>
2021-11-26 15:35:55 -03:00
# include <LibCore/System.h>
2023-03-21 12:35:30 -03:00
# include <LibFileSystem/FileSystem.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/Application.h>
2021-04-13 11:18:20 -03:00
# include <LibGUI/Menubar.h>
2022-12-17 22:48:17 -03:00
# include <LibGUI/MessageBox.h>
2021-02-10 15:02:43 -03:00
# include <LibGUI/Notification.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/Window.h>
2021-11-26 15:35:55 -03:00
# include <LibMain/Main.h>
2020-08-12 19:25:37 -03:00
# include <fcntl.h>
2020-06-28 14:40:10 -03:00
# include <spawn.h>
2019-10-21 13:46:55 -03:00
# include <stdio.h>
2020-08-12 19:25:37 -03:00
# include <sys/types.h>
2019-12-28 19:25:50 -03:00
# include <sys/wait.h>
2019-10-21 13:46:55 -03:00
# include <unistd.h>
2020-08-17 10:22:30 -03:00
using namespace HackStudio ;
2022-02-18 12:18:22 -03:00
static WeakPtr < HackStudioWidget > s_hack_studio_widget ;
2020-08-15 04:58:16 -03:00
2020-09-19 11:25:05 -03:00
static bool make_is_available ( ) ;
2022-12-17 22:48:17 -03:00
static ErrorOr < void > notify_make_not_available ( ) ;
2020-09-19 11:25:05 -03:00
static void update_path_environment_variable ( ) ;
2022-12-04 15:02:33 -03:00
static Optional < DeprecatedString > last_opened_project_path ( ) ;
2023-05-27 09:18:19 -03:00
static ErrorOr < NonnullRefPtr < HackStudioWidget > > create_hack_studio_widget ( bool mode_coredump , StringView path , pid_t pid_to_debug ) ;
2019-10-27 08:55:10 -03:00
2021-11-26 15:35:55 -03:00
ErrorOr < int > serenity_main ( Main : : Arguments arguments )
2019-10-21 13:46:55 -03:00
{
2021-11-27 19:26:34 -03:00
TRY ( Core : : System : : pledge ( " stdio recvfd sendfd tty rpath cpath wpath proc exec unix fattr thread ptrace " ) ) ;
2020-01-11 17:17:56 -03:00
2023-05-05 01:24:53 -03:00
auto app = TRY ( GUI : : Application : : create ( arguments ) ) ;
2023-04-01 08:08:29 -03:00
Config : : pledge_domains ( { " HackStudio " , " Terminal " , " FileManager " } ) ;
2019-10-21 13:46:55 -03:00
2021-09-18 11:12:29 -03:00
auto window = GUI : : Window : : construct ( ) ;
2023-09-21 13:05:11 -03:00
window - > restore_size_and_position ( " HackStudio " sv , " Window " sv , { { 840 , 600 } } ) ;
window - > save_size_and_position_on_close ( " HackStudio " sv , " Window " sv ) ;
2023-01-20 16:06:05 -03:00
auto icon = TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/app-hack-studio.png " sv ) ) ;
2022-12-17 22:48:17 -03:00
window - > set_icon ( icon ) ;
2019-11-05 17:07:39 -03:00
2020-09-19 11:25:05 -03:00
update_path_environment_variable ( ) ;
2020-01-15 23:42:54 -03:00
2021-02-10 15:02:43 -03:00
if ( ! make_is_available ( ) ) {
2022-12-17 22:48:17 -03:00
TRY ( notify_make_not_available ( ) ) ;
2021-02-10 15:02:43 -03:00
}
2019-12-28 19:25:50 -03:00
2023-02-28 17:41:43 -03:00
StringView path_argument ;
2021-11-19 11:13:07 -03:00
bool mode_coredump = false ;
2023-02-18 07:07:05 -03:00
pid_t pid_to_debug = - 1 ;
2020-08-12 18:23:45 -03:00
Core : : ArgsParser args_parser ;
2020-08-12 19:25:37 -03:00
args_parser . add_positional_argument ( path_argument , " Path to a workspace or a file " , " path " , Core : : ArgsParser : : Required : : No ) ;
2021-11-30 12:42:21 -03:00
args_parser . add_option ( mode_coredump , " Debug a coredump in HackStudio " , " coredump " , ' c ' ) ;
2023-02-18 07:07:05 -03:00
args_parser . add_option ( pid_to_debug , " Attach debugger to running process " , " pid " , ' p ' , " PID " ) ;
2021-11-26 15:35:55 -03:00
args_parser . parse ( arguments ) ;
2020-08-05 11:39:55 -03:00
2023-05-27 09:18:19 -03:00
auto hack_studio_widget = TRY ( create_hack_studio_widget ( mode_coredump , path_argument , pid_to_debug ) ) ;
2022-12-15 21:57:49 -03:00
window - > set_main_widget ( hack_studio_widget ) ;
2022-02-18 12:18:22 -03:00
s_hack_studio_widget = hack_studio_widget ;
2020-09-26 18:11:15 -03:00
2022-12-04 15:02:33 -03:00
window - > set_title ( DeprecatedString : : formatted ( " {} - Hack Studio " , hack_studio_widget - > project ( ) . name ( ) ) ) ;
2020-12-10 14:59:03 -03:00
2022-12-17 22:48:17 -03:00
TRY ( hack_studio_widget - > initialize_menubar ( * window ) ) ;
2020-04-04 07:18:40 -03:00
2021-09-18 11:12:29 -03:00
window - > on_close_request = [ & ] ( ) - > GUI : : Window : : CloseRequestDecision {
2022-02-18 12:18:22 -03:00
hack_studio_widget - > locator ( ) . close ( ) ;
if ( hack_studio_widget - > warn_unsaved_changes ( " There are unsaved changes, do you want to save before exiting? " ) = = HackStudioWidget : : ContinueDecision : : Yes )
2021-05-01 07:42:07 -03:00
return GUI : : Window : : CloseRequestDecision : : Close ;
return GUI : : Window : : CloseRequestDecision : : StayOpen ;
} ;
2021-09-18 11:12:29 -03:00
window - > show ( ) ;
2022-02-18 12:18:22 -03:00
hack_studio_widget - > update_actions ( ) ;
2019-10-21 17:13:20 -03:00
2021-11-19 11:13:07 -03:00
if ( mode_coredump )
2023-05-27 09:18:19 -03:00
hack_studio_widget - > open_coredump ( path_argument ) ;
2023-02-18 07:07:05 -03:00
if ( pid_to_debug ! = - 1 )
hack_studio_widget - > debug_process ( pid_to_debug ) ;
2021-11-19 11:13:07 -03:00
2020-07-04 09:05:19 -03:00
return app - > exec ( ) ;
2019-10-21 13:46:55 -03:00
}
2019-10-22 17:15:43 -03:00
2020-09-19 11:25:05 -03:00
static bool make_is_available ( )
2019-10-22 17:15:43 -03:00
{
2020-09-19 11:25:05 -03:00
pid_t pid ;
2022-04-01 14:58:27 -03:00
char const * argv [ ] = { " make " , " --version " , nullptr } ;
2020-09-19 11:25:05 -03:00
posix_spawn_file_actions_t action ;
posix_spawn_file_actions_init ( & action ) ;
posix_spawn_file_actions_addopen ( & action , STDOUT_FILENO , " /dev/null " , O_WRONLY , 0 ) ;
if ( ( errno = posix_spawnp ( & pid , " make " , & action , nullptr , const_cast < char * * > ( argv ) , environ ) ) ) {
perror ( " posix_spawn " ) ;
return false ;
}
int wstatus ;
waitpid ( pid , & wstatus , 0 ) ;
posix_spawn_file_actions_destroy ( & action ) ;
return WEXITSTATUS ( wstatus ) = = 0 ;
2019-10-22 17:15:43 -03:00
}
2019-10-22 17:18:46 -03:00
2022-12-17 22:48:17 -03:00
static ErrorOr < void > notify_make_not_available ( )
2021-02-10 15:02:43 -03:00
{
auto notification = GUI : : Notification : : construct ( ) ;
2023-01-20 16:06:05 -03:00
auto icon = TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/32x32/app-hack-studio.png " sv ) ) ;
2022-12-17 22:48:17 -03:00
notification - > set_icon ( icon ) ;
2023-08-31 13:50:32 -03:00
notification - > set_title ( " 'make' Not Available " _string ) ;
notification - > set_text ( " You probably want to install the binutils, gcc, and make ports from the root of the Serenity repository " _string ) ;
2021-02-10 15:02:43 -03:00
notification - > show ( ) ;
2022-12-17 22:48:17 -03:00
return { } ;
2021-02-10 15:02:43 -03:00
}
2020-09-19 11:25:05 -03:00
static void update_path_environment_variable ( )
2019-10-22 17:18:46 -03:00
{
2020-09-19 11:25:05 -03:00
StringBuilder path ;
2022-07-11 16:53:29 -03:00
auto const * path_env_ptr = getenv ( " PATH " ) ;
if ( path_env_ptr ! = NULL )
path . append ( { path_env_ptr , strlen ( path_env_ptr ) } ) ;
2020-09-19 11:25:05 -03:00
if ( path . length ( ) )
2022-07-11 17:10:18 -03:00
path . append ( ' : ' ) ;
2022-08-20 12:01:53 -03:00
path . append ( DEFAULT_PATH_SV ) ;
2022-12-05 22:12:49 -03:00
setenv ( " PATH " , path . to_deprecated_string ( ) . characters ( ) , true ) ;
2020-03-13 10:38:21 -03:00
}
2022-12-04 15:02:33 -03:00
static Optional < DeprecatedString > last_opened_project_path ( )
2022-02-13 18:34:28 -03:00
{
auto projects = HackStudioWidget : : read_recent_projects ( ) ;
if ( projects . size ( ) = = 0 )
return { } ;
2023-03-21 12:35:30 -03:00
if ( ! FileSystem : : exists ( projects [ 0 ] ) )
2022-02-13 18:34:28 -03:00
return { } ;
return { projects [ 0 ] } ;
}
2020-09-19 11:25:05 -03:00
namespace HackStudio {
2019-10-26 16:45:29 -03:00
2020-09-19 11:25:05 -03:00
GUI : : TextEditor & current_editor ( )
{
return s_hack_studio_widget - > current_editor ( ) ;
}
2019-10-27 08:55:10 -03:00
2022-12-04 15:02:33 -03:00
void open_file ( DeprecatedString const & filename )
2020-09-19 11:25:05 -03:00
{
2021-04-29 16:46:15 -03:00
s_hack_studio_widget - > open_file ( filename ) ;
2021-02-27 04:50:02 -03:00
}
2022-12-04 15:02:33 -03:00
void open_file ( DeprecatedString const & filename , size_t line , size_t column )
2021-02-27 04:50:02 -03:00
{
2021-08-27 10:18:00 -03:00
s_hack_studio_widget - > open_file ( filename , line , column ) ;
2019-10-23 15:54:41 -03:00
}
2019-12-28 19:25:50 -03:00
2020-09-19 11:25:05 -03:00
RefPtr < EditorWrapper > current_editor_wrapper ( )
2019-12-28 19:25:50 -03:00
{
2020-09-19 11:25:05 -03:00
if ( ! s_hack_studio_widget )
return nullptr ;
return s_hack_studio_widget - > current_editor_wrapper ( ) ;
}
2020-08-12 19:25:37 -03:00
2020-09-19 11:25:05 -03:00
Project & project ( )
{
return s_hack_studio_widget - > project ( ) ;
2019-12-28 19:25:50 -03:00
}
2020-08-17 10:22:30 -03:00
2022-12-04 15:02:33 -03:00
DeprecatedString currently_open_file ( )
2020-09-19 11:25:05 -03:00
{
if ( ! s_hack_studio_widget )
return { } ;
2021-05-01 07:04:19 -03:00
return s_hack_studio_widget - > active_file ( ) ;
2020-08-17 10:22:30 -03:00
}
2020-09-19 11:25:05 -03:00
void set_current_editor_wrapper ( RefPtr < EditorWrapper > wrapper )
2020-08-17 10:22:30 -03:00
{
2020-09-19 11:25:05 -03:00
s_hack_studio_widget - > set_current_editor_wrapper ( wrapper ) ;
}
2022-08-04 11:23:11 -03:00
void update_editor_window_title ( )
{
s_hack_studio_widget - > update_current_editor_title ( ) ;
s_hack_studio_widget - > update_window_title ( ) ;
}
2021-02-27 04:50:02 -03:00
Locator & locator ( )
{
return s_hack_studio_widget - > locator ( ) ;
}
2021-12-03 04:38:03 -03:00
void for_each_open_file ( Function < void ( ProjectFile const & ) > func )
{
s_hack_studio_widget - > for_each_open_file ( move ( func ) ) ;
}
2022-02-12 06:20:25 -03:00
bool semantic_syntax_highlighting_is_enabled ( )
{
return s_hack_studio_widget - > semantic_syntax_highlighting_is_enabled ( ) ;
}
2020-08-17 10:22:30 -03:00
}
2023-02-18 07:07:05 -03:00
2023-05-27 09:18:19 -03:00
static ErrorOr < NonnullRefPtr < HackStudioWidget > > create_hack_studio_widget ( bool mode_coredump , StringView raw_path_argument , pid_t pid_to_debug )
2023-02-18 07:07:05 -03:00
{
2023-05-27 09:18:19 -03:00
DeprecatedString project_path ;
if ( pid_to_debug ! = - 1 | | mode_coredump )
2023-02-18 07:07:05 -03:00
project_path = " /usr/src/serenity " ;
2023-05-27 09:18:19 -03:00
else if ( ! raw_path_argument . is_null ( ) )
// FIXME: Validation is unintentional, and should be removed when migrating to String.
project_path = TRY ( DeprecatedString : : from_utf8 ( raw_path_argument ) ) ;
else if ( auto last_path = last_opened_project_path ( ) ; last_path . has_value ( ) )
project_path = last_path . release_value ( ) ;
else
project_path = TRY ( FileSystem : : real_path ( " . " sv ) ) . to_deprecated_string ( ) ;
2023-02-18 07:07:05 -03:00
return HackStudioWidget : : create ( project_path ) ;
}