diff --git a/src/cmake.cc b/src/cmake.cc index b4b2268..06d9e07 100644 --- a/src/cmake.cc +++ b/src/cmake.cc @@ -162,7 +162,7 @@ void CMake::parse_variable_parameters(std::string &data) { pos++; } for(auto &var: variables) { - auto pos=data.find("${"+var.first+'}'); //TODO: check if there is a slash in front of $ + auto pos=data.find("${"+var.first+'}'); while(pos!=std::string::npos) { data.replace(pos, var.first.size()+3, var.second); pos=data.find("${"+var.first+'}'); @@ -170,7 +170,7 @@ void CMake::parse_variable_parameters(std::string &data) { } //Remove variables we do not know: - pos=data.find("${"); //TODO: check if there is a slash in front of $ + pos=data.find("${"); auto pos_end=data.find("}", pos+2); while(pos!=std::string::npos && pos_end!=std::string::npos) { data.erase(pos, pos_end-pos+1); @@ -221,7 +221,7 @@ std::vector CMake::get_function_parameters(std::string &data) { parameters.emplace_back(data.substr(parameter_pos)); for(auto &var: variables) { for(auto ¶meter: parameters) { - auto pos=parameter.find("${"+var.first+'}'); //TODO: check if there is a slash in front of $ + auto pos=parameter.find("${"+var.first+'}'); while(pos!=std::string::npos) { parameter.replace(pos, var.first.size()+3, var.second); pos=parameter.find("${"+var.first+'}'); diff --git a/src/directories.cc b/src/directories.cc index 1d2d735..088a0f7 100644 --- a/src/directories.cc +++ b/src/directories.cc @@ -58,7 +58,7 @@ void Directories::open_folder(const boost::filesystem::path& dir_path) { tree_store->clear(); - if(current_path!=new_path) + if(dir_path!="") cmake=std::unique_ptr(new CMake(new_path)); auto project=cmake->get_functions_parameters("project"); if(project.size()>0 && project[0].second.size()>0) @@ -69,8 +69,11 @@ void Directories::open_folder(const boost::filesystem::path& dir_path) { for(auto &path: expanded_paths) tree_view.expand_row(path, false); - + current_path=new_path; + + if(selected_path.size()>0) + select_path(selected_path); DEBUG("Folder opened"); } @@ -80,6 +83,7 @@ void Directories::select_path(const std::string &path) { auto tree_path=Gtk::TreePath(iter); tree_view.expand_to_path(tree_path); tree_view.set_cursor(tree_path); + selected_path=path; return true; } return false; diff --git a/src/directories.h b/src/directories.h index 322aba6..57d36da 100644 --- a/src/directories.h +++ b/src/directories.h @@ -41,6 +41,7 @@ private: Gtk::TreeView tree_view; Glib::RefPtr tree_store; ColumnRecord column_record; + std::string selected_path; }; #endif // JUCI_DIRECTORIES_H_ diff --git a/src/terminal.cc b/src/terminal.cc index d406656..87f1427 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -2,6 +2,67 @@ #include #include "logging.h" #include "singletons.h" +#include +#include + +#include //TODO: remove +using namespace std; //TODO: remove + +#define READ 0 +#define WRITE 1 + +//TODO: Windows... +//Coppied partially from http://www.linuxprogrammingblog.com/code-examples/sigaction +void signal_execl_exit(int sig, siginfo_t *siginfo, void *context) { + int status; + while (waitpid(siginfo->si_pid, &status, WNOHANG) > 0) {} + + Singleton::terminal()->async_pid_mutex.lock(); + if(Singleton::terminal()->async_pid_descriptors.find(siginfo->si_pid)!=Singleton::terminal()->async_pid_descriptors.end()) { + Singleton::terminal()->async_pid_status[siginfo->si_pid]=status; + close(Singleton::terminal()->async_pid_descriptors.at(siginfo->si_pid).first); + close(Singleton::terminal()->async_pid_descriptors.at(siginfo->si_pid).second); + Singleton::terminal()->async_pid_descriptors.erase(siginfo->si_pid); + } + Singleton::terminal()->async_pid_mutex.unlock(); +} + +//TODO: Windows... +//Copied from http://stackoverflow.com/questions/12778672/killing-process-that-has-been-created-with-popen2 +pid_t popen2(const char *command, int *input_descriptor, int *output_descriptor) { + pid_t pid; + int p_stdin[2], p_stdout[2]; + + if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0) + return -1; + + pid = fork(); + + if (pid < 0) + return pid; + else if (pid == 0) { + close(p_stdin[WRITE]); + dup2(p_stdin[READ], READ); + close(p_stdout[READ]); + dup2(p_stdout[WRITE], WRITE); + + execl("/bin/sh", "sh", "-c", command, NULL); + perror("execl"); + exit(1); + } + + if (input_descriptor == NULL) + close(p_stdin[WRITE]); + else + *input_descriptor = p_stdin[WRITE]; + + if (output_descriptor == NULL) + close(p_stdout[READ]); + else + *output_descriptor = p_stdout[READ]; + + return pid; +} Terminal::InProgress::InProgress(const std::string& start_msg): stop(false) { waiting_print.connect([this](){ @@ -55,9 +116,20 @@ Terminal::Terminal() { }); async_execute_print.connect([this](){ - print(async_execute_print_string); - async_execute_print_finished=true; + async_execute_print_string_mutex.lock(); + if(async_execute_print_string.size()>0) { + print(async_execute_print_string); + async_execute_print_string=""; + } + async_execute_print_string_mutex.unlock(); }); + + //Coppied from http://www.linuxprogrammingblog.com/code-examples/sigaction + struct sigaction act; + memset (&act, '\0', sizeof(act)); + act.sa_sigaction = &signal_execl_exit; + act.sa_flags = SA_SIGINFO; + sigaction(SIGCHLD, &act, NULL); } bool Terminal::execute(const std::string &command, const std::string &path) { @@ -97,28 +169,36 @@ void Terminal::async_execute(const std::string &command, const std::string &path boost_path=boost::filesystem::path(path); //TODO: Windows... - cd_path_and_command="cd "+boost_path.string()+" 2>&1 && "+command; + cd_path_and_command="cd "+boost_path.string()+" 2>&1 && exec "+command; } else cd_path_and_command=command; - FILE* p; - p = popen(cd_path_and_command.c_str(), "r"); - if (p == NULL) { - async_execute_print_string="Error: Failed to run command" + command + "\n"; - async_execute_print_finished=false; + int input_descriptor, output_descriptor; + async_pid_mutex.lock(); + auto pid=popen2(cd_path_and_command.c_str(), &input_descriptor, &output_descriptor); + async_pid_descriptors[pid]={input_descriptor, output_descriptor}; + async_pid_mutex.unlock(); + if (pid<=0) { + async_execute_print_string_mutex.lock(); + async_execute_print_string+="Error: Failed to run command" + command + "\n"; + async_execute_print_string_mutex.unlock(); async_execute_print(); } else { char buffer[1024]; - while (fgets(buffer, 1024, p) != NULL) { - async_execute_print_string=buffer; - async_execute_print_finished=false; + ssize_t n; + while ((n=read(output_descriptor, buffer, 1024)) > 0) { + async_execute_print_string_mutex.lock(); + for(int c=0;cinsert(text_view.get_buffer()->end(), "> "+message); + text_view.get_buffer()->insert(text_view.get_buffer()->end(), message); return text_view.get_buffer()->end().get_line(); } diff --git a/src/terminal.h b/src/terminal.h index fe7c4f0..493ffbd 100644 --- a/src/terminal.h +++ b/src/terminal.h @@ -7,6 +7,7 @@ #include #include #include +#include class Terminal : public Gtk::HBox { public: @@ -27,6 +28,10 @@ public: Terminal(); bool execute(const std::string &command, const std::string &path=""); void async_execute(const std::string &command, const std::string &path="", std::function callback=nullptr); + std::unordered_map > async_pid_descriptors; + std::unordered_map async_pid_status; + std::mutex async_pid_mutex; + int print(std::string message); void print(int line_nr, std::string message); std::shared_ptr print_in_progress(std::string start_msg); @@ -36,7 +41,7 @@ private: Glib::Dispatcher async_execute_print; std::string async_execute_print_string; - std::atomic async_execute_print_finished; + std::mutex async_execute_print_string_mutex; }; #endif // JUCI_TERMINAL_H_ diff --git a/src/window.cc b/src/window.cc index 04a2fe3..9f8ecd2 100644 --- a/src/window.cc +++ b/src/window.cc @@ -88,6 +88,17 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil notebook.signal_page_removed().connect([this](Gtk::Widget* page, guint page_num) { entry_box.hide(); }); + + compile_success.connect([this](){ + directories.open_folder(); + }); + run_success.connect([this](){ + Singleton::terminal()->print("Execution returned: success\n"); + }); + run_error.connect([this](){ + Singleton::terminal()->print("Execution returned: error\n"); + }); + INFO("Window created"); } // Window constructor @@ -197,6 +208,7 @@ void Window::create_menu() { if(notebook.get_current_page()==-1 || compiling) return; CMake cmake(notebook.get_current_view()->file_path); + directories.open_folder(); auto executables = cmake.get_functions_parameters("add_executable"); std::string executable; boost::filesystem::path path; @@ -207,29 +219,40 @@ void Window::create_menu() { } if(cmake.project_path!="") { compiling=true; - if(path!="") + if(path!="") { Singleton::terminal()->print("Compiling and executing "+path.string()+"\n"); + //TODO: Windows... + Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this, path](bool success){ + compiling=false; + if(success) { + compile_success(); + //TODO: Windows... + Singleton::terminal()->async_execute(path.string()+" 2>&1", path.parent_path().string(), [this](bool success){ + if(success) + run_success(); + else + run_error(); + }); + } + }); + } else Singleton::terminal()->print("Could not find an executable, please use add_executable in CMakeLists.txt\n"); - //TODO: Windows... - Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this, path](int exit_code){ - compiling=false; - if(path!="") - //TODO: Windows... - Singleton::terminal()->async_execute(path.string()+" 2>&1", path.parent_path().string()); - }); } }); menu.action_group->add(Gtk::Action::create("ProjectCompile", "Compile"), Gtk::AccelKey(menu.key_map["compile"]), [this]() { if(notebook.get_current_page()==-1 || compiling) return; CMake cmake(notebook.get_current_view()->file_path); + directories.open_folder(); if(cmake.project_path!="") { compiling=true; Singleton::terminal()->print("Compiling project "+cmake.project_path.string()+"\n"); //TODO: Windows... - Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this](int exit_code){ + Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this](bool success){ compiling=false; + if(success) + compile_success(); }); } }); @@ -243,8 +266,15 @@ void Window::create_menu() { } bool Window::on_key_press_event(GdkEventKey *event) { - if(event->keyval==GDK_KEY_Escape) + if(event->keyval==GDK_KEY_Escape) { + if(entry_box.entries.size()==0) { + Singleton::terminal()->async_pid_mutex.lock(); + for(auto &pid: Singleton::terminal()->async_pid_descriptors) + kill(pid.first, SIGTERM); + Singleton::terminal()->async_pid_mutex.unlock(); + } entry_box.hide(); + } #ifdef __APPLE__ //For Apple's Command-left, right, up, down keys else if((event->state & GDK_META_MASK)>0) { if(event->keyval==GDK_KEY_Left) { @@ -336,8 +366,6 @@ void Window::open_folder_dialog() { if(result==Gtk::RESPONSE_OK) { std::string project_path=dialog.get_filename(); directories.open_folder(project_path); - if(notebook.get_current_page()!=-1) - directories.select_path(notebook.get_current_view()->file_path); } } diff --git a/src/window.h b/src/window.h index 42ee93c..ea53f6b 100644 --- a/src/window.h +++ b/src/window.h @@ -26,6 +26,8 @@ private: EntryBox entry_box; Menu menu; std::atomic compiling; + Glib::Dispatcher compile_success; + Glib::Dispatcher run_success, run_error; void create_menu(); void hide();