mirror of https://gitlab.com/cppit/jucipp
13 changed files with 524 additions and 977 deletions
@ -0,0 +1,21 @@
|
||||
#include "process.h" |
||||
|
||||
#include <iostream> //TODO: remove |
||||
using namespace std; //TODO: remove
|
||||
|
||||
Process::Process(const std::string &command, const std::string &path, |
||||
std::function<void(const char* bytes, size_t n)> read_stdout, |
||||
std::function<void(const char* bytes, size_t n)> read_stderr, |
||||
bool use_stdin, size_t buffer_size): |
||||
read_stdout(read_stdout), read_stderr(read_stderr), use_stdin(use_stdin), buffer_size(buffer_size) { |
||||
id=open(command, path); |
||||
if(id>0) |
||||
async_read(); |
||||
} |
||||
|
||||
Process::~Process() { |
||||
if(stdout_thread.joinable()) |
||||
stdout_thread.join(); |
||||
if(stderr_thread.joinable()) |
||||
stderr_thread.join(); |
||||
} |
||||
@ -0,0 +1,52 @@
|
||||
#ifndef JUCI_PROCESS_H_ |
||||
#define JUCI_PROCESS_H_ |
||||
|
||||
#include <sys/wait.h> |
||||
#include <string> |
||||
#include <functional> |
||||
#include <vector> |
||||
#include <mutex> |
||||
#include <thread> |
||||
|
||||
#ifdef _WIN32 |
||||
|
||||
#else |
||||
typedef pid_t process_id_type; |
||||
typedef int file_descriptor_type; |
||||
typedef int exit_code_type; |
||||
#endif |
||||
|
||||
class Process { |
||||
public: |
||||
Process(const std::string &command, const std::string &path=std::string(), |
||||
std::function<void(const char *bytes, size_t n)> read_stdout=nullptr, |
||||
std::function<void(const char *bytes, size_t n)> read_stderr=nullptr, |
||||
bool use_stdin=false, |
||||
size_t buffer_size=131072); |
||||
~Process(); |
||||
|
||||
///Get the process id of the started process.
|
||||
process_id_type get_id() {return id;} |
||||
///Wait until process is finished, and return exit_code.
|
||||
exit_code_type get_exit_code(); |
||||
bool write(const char *bytes, size_t n); |
||||
|
||||
///Kill a given process id.
|
||||
static void kill(process_id_type id, bool force=false); |
||||
|
||||
private: |
||||
std::function<void(const char* bytes, size_t n)> read_stdout; |
||||
std::function<void(const char* bytes, size_t n)> read_stderr; |
||||
std::thread stdout_thread, stderr_thread; |
||||
bool use_stdin; |
||||
std::mutex stdin_mutex; |
||||
const size_t buffer_size; |
||||
|
||||
std::unique_ptr<file_descriptor_type> stdout_fd, stderr_fd, stdin_fd; |
||||
|
||||
process_id_type open(const std::string &command, const std::string &path); |
||||
process_id_type id; |
||||
void async_read(); |
||||
}; |
||||
|
||||
#endif // JUCI_PROCESS_H_
|
||||
@ -0,0 +1,151 @@
|
||||
#include "process.h" |
||||
#include <cstdlib> |
||||
#include <thread> |
||||
#include <signal.h> |
||||
|
||||
#include <iostream> //TODO: remove |
||||
using namespace std; //TODO: remove
|
||||
|
||||
pid_t Process::open(const std::string &command, const std::string &path) { |
||||
if(use_stdin) |
||||
stdin_fd=std::unique_ptr<file_descriptor_type>(new int); |
||||
if(read_stdout) |
||||
stdout_fd=std::unique_ptr<file_descriptor_type>(new int); |
||||
if(read_stderr) |
||||
stderr_fd=std::unique_ptr<file_descriptor_type>(new int); |
||||
|
||||
int stdin_p[2], stdout_p[2], stderr_p[2]; |
||||
|
||||
if(stdin_fd && pipe(stdin_p)!=0) { |
||||
close(stdin_p[0]); |
||||
close(stdin_p[1]); |
||||
return -1; |
||||
} |
||||
if(stdout_fd && pipe(stdout_p)!=0) { |
||||
if(stdin_fd) close(stdin_p[0]); |
||||
if(stdin_fd) close(stdin_p[1]); |
||||
close(stdout_p[0]); |
||||
close(stdout_p[1]); |
||||
return -1; |
||||
} |
||||
if(stderr_fd && pipe(stderr_p)!=0) { |
||||
if(stdin_fd) close(stdin_p[0]); |
||||
if(stdin_fd) close(stdin_p[1]); |
||||
if(stdout_fd) close(stdout_p[0]); |
||||
if(stdout_fd) close(stdout_p[1]); |
||||
close(stderr_p[0]); |
||||
close(stderr_p[1]); |
||||
return -1; |
||||
} |
||||
|
||||
pid_t pid = fork(); |
||||
|
||||
if (pid < 0) { |
||||
if(stdin_fd) close(stdin_p[0]); |
||||
if(stdin_fd) close(stdin_p[1]); |
||||
if(stdout_fd) close(stdout_p[0]); |
||||
if(stdout_fd) close(stdout_p[1]); |
||||
if(stderr_fd) close(stderr_p[0]); |
||||
if(stderr_fd) close(stderr_p[1]); |
||||
return pid; |
||||
} |
||||
else if (pid == 0) { |
||||
if(stdin_fd) close(stdin_p[1]); |
||||
if(stdout_fd) close(stdout_p[0]); |
||||
if(stderr_fd) close(stderr_p[0]); |
||||
if(stdin_fd) dup2(stdin_p[0], 0); |
||||
if(stdout_fd) dup2(stdout_p[1], 1); |
||||
if(stderr_fd) dup2(stderr_p[1], 2); |
||||
|
||||
setpgid(0, 0); |
||||
//TODO: See here on how to emulate tty for colors: http://stackoverflow.com/questions/1401002/trick-an-application-into-thinking-its-stdin-is-interactive-not-a-pipe
|
||||
//TODO: One solution is: echo "command;exit"|script -q /dev/null
|
||||
|
||||
if(!path.empty()) |
||||
execl("/bin/sh", "sh", "-c", ("cd \""+path+"\" && "+command).c_str(), NULL); |
||||
else |
||||
execl("/bin/sh", "sh", "-c", command.c_str(), NULL); |
||||
|
||||
perror("execl"); |
||||
exit(EXIT_FAILURE); |
||||
} |
||||
|
||||
if(stdin_fd) close(stdin_p[0]); |
||||
if(stdout_fd) close(stdout_p[1]); |
||||
if(stderr_fd) close(stderr_p[1]); |
||||
|
||||
if(stdin_fd) *stdin_fd = stdin_p[1]; |
||||
if(stdout_fd) *stdout_fd = stdout_p[0]; |
||||
if(stderr_fd) *stderr_fd = stderr_p[0]; |
||||
|
||||
return pid; |
||||
} |
||||
|
||||
void Process::async_read() { |
||||
if(stdout_fd) { |
||||
stdout_thread=std::thread([this](){ |
||||
char buffer[buffer_size]; |
||||
ssize_t n; |
||||
while ((n=read(*stdout_fd, buffer, buffer_size)) > 0) |
||||
read_stdout(buffer, static_cast<size_t>(n)); |
||||
}); |
||||
} |
||||
if(stderr_fd) { |
||||
stderr_thread=std::thread([this](){ |
||||
char buffer[buffer_size]; |
||||
ssize_t n; |
||||
while ((n=read(*stderr_fd, buffer, buffer_size)) > 0) |
||||
read_stderr(buffer, static_cast<size_t>(n)); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
int Process::get_exit_code() { |
||||
int exit_code; |
||||
waitpid(id, &exit_code, 0); |
||||
|
||||
if(stdout_thread.joinable()) |
||||
stdout_thread.join(); |
||||
if(stderr_thread.joinable()) |
||||
stderr_thread.join(); |
||||
|
||||
stdin_mutex.lock(); |
||||
if(stdin_fd) { |
||||
close(*stdin_fd); |
||||
stdin_fd.reset(); |
||||
} |
||||
stdin_mutex.unlock(); |
||||
if(stdout_fd) { |
||||
close(*stdout_fd); |
||||
stdout_fd.reset(); |
||||
} |
||||
if(stderr_fd) { |
||||
close(*stderr_fd); |
||||
stderr_fd.reset(); |
||||
} |
||||
|
||||
return exit_code; |
||||
} |
||||
|
||||
bool Process::write(const char *bytes, size_t n) { |
||||
stdin_mutex.lock(); |
||||
if(stdin_fd) { |
||||
if(::write(*stdin_fd, bytes, n)>=0) { |
||||
stdin_mutex.unlock(); |
||||
return true; |
||||
} |
||||
else { |
||||
stdin_mutex.unlock(); |
||||
return false; |
||||
} |
||||
} |
||||
stdin_mutex.unlock(); |
||||
return false; |
||||
} |
||||
|
||||
void Process::kill(process_id_type id, bool force) { |
||||
if(force) |
||||
::kill(-id, SIGTERM); |
||||
else |
||||
::kill(-id, SIGINT); |
||||
} |
||||
@ -0,0 +1,3 @@
|
||||
#include "process.h" |
||||
#include <thread> |
||||
#include <windows.h> |
||||
@ -0,0 +1,273 @@
|
||||
#include "terminal.h" |
||||
#include <iostream> |
||||
#include "logging.h" |
||||
#include "singletons.h" |
||||
#include "process.h" |
||||
|
||||
#include <iostream> //TODO: remove |
||||
using namespace std; //TODO: remove
|
||||
|
||||
Terminal::InProgress::InProgress(const std::string& start_msg): stop(false) { |
||||
waiting_print.connect([this](){ |
||||
Singleton::terminal->async_print(line_nr-1, "."); |
||||
}); |
||||
start(start_msg); |
||||
} |
||||
|
||||
Terminal::InProgress::~InProgress() { |
||||
stop=true; |
||||
if(wait_thread.joinable()) |
||||
wait_thread.join(); |
||||
} |
||||
|
||||
void Terminal::InProgress::start(const std::string& msg) { |
||||
line_nr=Singleton::terminal->print(msg+"...\n"); |
||||
wait_thread=std::thread([this](){ |
||||
size_t c=0; |
||||
while(!stop) { |
||||
if(c%100==0) |
||||
waiting_print(); |
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10)); |
||||
c++; |
||||
} |
||||
}); |
||||
} |
||||
|
||||
void Terminal::InProgress::done(const std::string& msg) { |
||||
if(!stop) { |
||||
stop=true; |
||||
Singleton::terminal->async_print(line_nr-1, msg); |
||||
} |
||||
} |
||||
|
||||
void Terminal::InProgress::cancel(const std::string& msg) { |
||||
if(!stop) { |
||||
stop=true; |
||||
Singleton::terminal->async_print(line_nr-1, msg); |
||||
} |
||||
} |
||||
|
||||
Terminal::Terminal() { |
||||
bold_tag=get_buffer()->create_tag(); |
||||
bold_tag->property_weight()=PANGO_WEIGHT_BOLD; |
||||
|
||||
async_print_dispatcher.connect([this](){ |
||||
async_print_strings_mutex.lock(); |
||||
if(async_print_strings.size()>0) { |
||||
for(auto &string_bold: async_print_strings) |
||||
print(string_bold.first, string_bold.second); |
||||
async_print_strings.clear(); |
||||
} |
||||
async_print_strings_mutex.unlock(); |
||||
}); |
||||
async_print_on_line_dispatcher.connect([this](){ |
||||
async_print_on_line_strings_mutex.lock(); |
||||
if(async_print_on_line_strings.size()>0) { |
||||
for(auto &line_string: async_print_on_line_strings) |
||||
print(line_string.first, line_string.second); |
||||
async_print_on_line_strings.clear(); |
||||
} |
||||
async_print_on_line_strings_mutex.unlock(); |
||||
}); |
||||
} |
||||
|
||||
int Terminal::process(const std::string &command, const boost::filesystem::path &path, bool use_pipes) {
|
||||
std::unique_ptr<Process> process; |
||||
if(use_pipes) |
||||
process=std::unique_ptr<Process>(new Process(command, path.string(), [this](const char* bytes, size_t n) { |
||||
async_print(std::string(bytes, n)); |
||||
}, [this](const char* bytes, size_t n) { |
||||
async_print(std::string(bytes, n), true); |
||||
})); |
||||
else |
||||
process=std::unique_ptr<Process>(new Process(command, path.string())); |
||||
|
||||
if(process->get_id()<=0) { |
||||
async_print("Error: Failed to run command: " + command + "\n", true); |
||||
return -1; |
||||
} |
||||
|
||||
return process->get_exit_code(); |
||||
} |
||||
|
||||
int Terminal::process(std::istream &stdin_stream, std::ostream &stdout_stream, const std::string &command, const boost::filesystem::path &path) { |
||||
Process process(command, path.string(), [this, &stdout_stream](const char* bytes, size_t n) { |
||||
Glib::ustring umessage(bytes, n); |
||||
Glib::ustring::iterator iter; |
||||
while(!umessage.validate(iter)) { |
||||
auto next_char_iter=iter; |
||||
next_char_iter++; |
||||
umessage.replace(iter, next_char_iter, "?"); |
||||
} |
||||
stdout_stream.write(umessage.data(), n); |
||||
}, [this](const char* bytes, size_t n) { |
||||
async_print(std::string(bytes, n), true); |
||||
}); |
||||
|
||||
if(process.get_id()<=0) { |
||||
async_print("Error: Failed to run command: " + command + "\n", true); |
||||
return -1; |
||||
} |
||||
|
||||
char buffer[131072]; |
||||
for(;;) { |
||||
stdin_stream.readsome(buffer, 131072); |
||||
auto read_n=stdin_stream.gcount(); |
||||
if(read_n==0) |
||||
break; |
||||
if(!process.write(buffer, read_n)) |
||||
break; |
||||
} |
||||
|
||||
return process.get_exit_code(); |
||||
} |
||||
|
||||
void Terminal::async_process(const std::string &command, const boost::filesystem::path &path, std::function<void(int exit_code)> callback) { |
||||
std::thread async_execute_thread([this, command, path, callback](){
|
||||
processes_mutex.lock(); |
||||
stdin_buffer.clear(); |
||||
std::shared_ptr<Process> process(new Process(command, path.string(), [this](const char* bytes, size_t n) { |
||||
async_print(std::string(bytes, n)); |
||||
}, [this](const char* bytes, size_t n) { |
||||
async_print(std::string(bytes, n), true); |
||||
}, true)); |
||||
auto pid=process->get_id(); |
||||
if (pid<=0) { |
||||
processes_mutex.unlock(); |
||||
async_print("Error: Failed to run command: " + command + "\n", true); |
||||
if(callback) |
||||
callback(-1); |
||||
return; |
||||
} |
||||
else { |
||||
processes.emplace_back(process); |
||||
processes_mutex.unlock(); |
||||
} |
||||
|
||||
exit_code_type exit_code=process->get_exit_code(); |
||||
|
||||
processes_mutex.lock(); |
||||
for(auto it=processes.begin();it!=processes.end();it++) { |
||||
if((*it)->get_id()==pid) { |
||||
processes.erase(it); |
||||
break; |
||||
} |
||||
} |
||||
stdin_buffer.clear(); |
||||
processes_mutex.unlock(); |
||||
|
||||
if(callback) |
||||
callback(exit_code); |
||||
}); |
||||
async_execute_thread.detach(); |
||||
} |
||||
|
||||
void Terminal::kill_last_async_process(bool force) { |
||||
processes_mutex.lock(); |
||||
if(processes.size()>0) |
||||
Process::kill(processes.back()->get_id(), force); |
||||
processes_mutex.unlock(); |
||||
} |
||||
|
||||
void Terminal::kill_async_processes(bool force) { |
||||
processes_mutex.lock(); |
||||
for(auto &process: processes) |
||||
Process::kill(process->get_id(), force); |
||||
processes_mutex.unlock(); |
||||
} |
||||
|
||||
size_t Terminal::print(const std::string &message, bool bold){ |
||||
Glib::ustring umessage=message; |
||||
Glib::ustring::iterator iter; |
||||
while(!umessage.validate(iter)) { |
||||
auto next_char_iter=iter; |
||||
next_char_iter++; |
||||
umessage.replace(iter, next_char_iter, "?"); |
||||
} |
||||
|
||||
if(bold) |
||||
get_buffer()->insert_with_tag(get_buffer()->end(), umessage, bold_tag); |
||||
else |
||||
get_buffer()->insert(get_buffer()->end(), umessage); |
||||
|
||||
if(get_buffer()->get_line_count()>Singleton::config->terminal.history_size) { |
||||
int lines=get_buffer()->get_line_count()-Singleton::config->terminal.history_size; |
||||
get_buffer()->erase(get_buffer()->begin(), get_buffer()->get_iter_at_line(lines)); |
||||
deleted_lines+=static_cast<size_t>(lines); |
||||
} |
||||
|
||||
return static_cast<size_t>(get_buffer()->end().get_line())+deleted_lines; |
||||
} |
||||
|
||||
void Terminal::print(size_t line_nr, const std::string &message){ |
||||
if(line_nr<deleted_lines) |
||||
return; |
||||
|
||||
Glib::ustring umessage=message; |
||||
Glib::ustring::iterator iter; |
||||
while(!umessage.validate(iter)) { |
||||
auto next_char_iter=iter; |
||||
next_char_iter++; |
||||
umessage.replace(iter, next_char_iter, "?"); |
||||
} |
||||
|
||||
auto end_line_iter=get_buffer()->get_iter_at_line(static_cast<int>(line_nr-deleted_lines)); |
||||
while(!end_line_iter.ends_line() && end_line_iter.forward_char()) {} |
||||
get_buffer()->insert(end_line_iter, umessage); |
||||
} |
||||
|
||||
std::shared_ptr<Terminal::InProgress> Terminal::print_in_progress(std::string start_msg) { |
||||
std::shared_ptr<Terminal::InProgress> in_progress=std::shared_ptr<Terminal::InProgress>(new Terminal::InProgress(start_msg)); |
||||
return in_progress; |
||||
} |
||||
|
||||
void Terminal::async_print(const std::string &message, bool bold) { |
||||
async_print_strings_mutex.lock(); |
||||
bool dispatch=true; |
||||
if(async_print_strings.size()>0) |
||||
dispatch=false; |
||||
async_print_strings.emplace_back(message, bold); |
||||
async_print_strings_mutex.unlock(); |
||||
if(dispatch) |
||||
async_print_dispatcher(); |
||||
} |
||||
|
||||
void Terminal::async_print(int line_nr, const std::string &message) { |
||||
async_print_on_line_strings_mutex.lock(); |
||||
bool dispatch=true; |
||||
if(async_print_on_line_strings.size()>0) |
||||
dispatch=false; |
||||
async_print_on_line_strings.emplace_back(line_nr, message); |
||||
async_print_on_line_strings_mutex.unlock(); |
||||
if(dispatch) |
||||
async_print_on_line_dispatcher(); |
||||
} |
||||
|
||||
bool Terminal::on_key_press_event(GdkEventKey *event) { |
||||
processes_mutex.lock(); |
||||
if(processes.size()>0) { |
||||
get_buffer()->place_cursor(get_buffer()->end()); |
||||
auto unicode=gdk_keyval_to_unicode(event->keyval); |
||||
char chr=(char)unicode; |
||||
if(unicode>=32 && unicode<=126) { |
||||
stdin_buffer+=chr; |
||||
get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1)); |
||||
} |
||||
else if(event->keyval==GDK_KEY_BackSpace) { |
||||
if(stdin_buffer.size()>0 && get_buffer()->get_char_count()>0) { |
||||
auto iter=get_buffer()->end(); |
||||
iter--; |
||||
stdin_buffer.pop_back(); |
||||
get_buffer()->erase(iter, get_buffer()->end()); |
||||
} |
||||
} |
||||
else if(event->keyval==GDK_KEY_Return) { |
||||
stdin_buffer+='\n'; |
||||
processes.back()->write(stdin_buffer.c_str(), stdin_buffer.size()); |
||||
get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1)); |
||||
stdin_buffer.clear(); |
||||
} |
||||
} |
||||
processes_mutex.unlock(); |
||||
return true; |
||||
} |
||||
@ -1,440 +0,0 @@
|
||||
#include "terminal.h" |
||||
#include <iostream> |
||||
#include "logging.h" |
||||
#include "singletons.h" |
||||
#include <unistd.h> |
||||
#include <sys/wait.h> |
||||
|
||||
#include <iostream> //TODO: remove |
||||
using namespace std; //TODO: remove
|
||||
|
||||
const ssize_t buffer_size=131072; |
||||
|
||||
//A working implementation of popen3, with all pipes getting closed properly.
|
||||
//TODO: Eidheim is going to publish this one on his github, along with example uses
|
||||
pid_t popen3(const std::string &command, const std::string &path, int *stdin_fd, int *stdout_fd, int *stderr_fd) { |
||||
pid_t pid; |
||||
int stdin_p[2], stdout_p[2], stderr_p[2]; |
||||
|
||||
if(stdin_fd!=nullptr && pipe(stdin_p)!=0) { |
||||
close(stdin_p[0]); |
||||
close(stdin_p[1]); |
||||
return -1; |
||||
} |
||||
if(stdout_fd!=nullptr && pipe(stdout_p)!=0) { |
||||
if(stdin_fd!=nullptr) close(stdin_p[0]); |
||||
if(stdin_fd!=nullptr) close(stdin_p[1]); |
||||
close(stdout_p[0]); |
||||
close(stdout_p[1]); |
||||
return -1; |
||||
} |
||||
if(stderr_fd!=nullptr && pipe(stderr_p)!=0) { |
||||
if(stdin_fd!=nullptr) close(stdin_p[0]); |
||||
if(stdin_fd!=nullptr) close(stdin_p[1]); |
||||
if(stdout_fd!=nullptr) close(stdout_p[0]); |
||||
if(stdout_fd!=nullptr) close(stdout_p[1]); |
||||
close(stderr_p[0]); |
||||
close(stderr_p[1]); |
||||
return -1; |
||||
} |
||||
|
||||
pid = fork(); |
||||
|
||||
if (pid < 0) { |
||||
if(stdin_fd!=nullptr) close(stdin_p[0]); |
||||
if(stdin_fd!=nullptr) close(stdin_p[1]); |
||||
if(stdout_fd!=nullptr) close(stdout_p[0]); |
||||
if(stdout_fd!=nullptr) close(stdout_p[1]); |
||||
if(stderr_fd!=nullptr) close(stderr_p[0]); |
||||
if(stderr_fd!=nullptr) close(stderr_p[1]); |
||||
return pid; |
||||
} |
||||
else if (pid == 0) { |
||||
if(stdin_fd!=nullptr) close(stdin_p[1]); |
||||
if(stdout_fd!=nullptr) close(stdout_p[0]); |
||||
if(stderr_fd!=nullptr) close(stderr_p[0]); |
||||
if(stdin_fd!=nullptr) dup2(stdin_p[0], 0); |
||||
if(stdout_fd!=nullptr) dup2(stdout_p[1], 1); |
||||
if(stderr_fd!=nullptr) dup2(stderr_p[1], 2); |
||||
|
||||
setpgid(0, 0); |
||||
//TODO: See here on how to emulate tty for colors: http://stackoverflow.com/questions/1401002/trick-an-application-into-thinking-its-stdin-is-interactive-not-a-pipe
|
||||
//TODO: One solution is: echo "command;exit"|script -q /dev/null
|
||||
std::string cd_path_and_command; |
||||
if(path!="") { |
||||
cd_path_and_command="cd \""+path+"\" && "+command; |
||||
} |
||||
else |
||||
cd_path_and_command=command; |
||||
execl("/bin/sh", "sh", "-c", cd_path_and_command.c_str(), NULL); |
||||
perror("execl"); |
||||
exit(EXIT_FAILURE); |
||||
} |
||||
|
||||
if(stdin_fd!=nullptr) close(stdin_p[0]); |
||||
if(stdout_fd!=nullptr) close(stdout_p[1]); |
||||
if(stderr_fd!=nullptr) close(stderr_p[1]); |
||||
|
||||
if(stdin_fd!=nullptr) *stdin_fd = stdin_p[1]; |
||||
if(stdout_fd!=nullptr) *stdout_fd = stdout_p[0]; |
||||
if(stderr_fd!=nullptr) *stderr_fd = stderr_p[0]; |
||||
|
||||
return pid; |
||||
} |
||||
|
||||
Terminal::InProgress::InProgress(const std::string& start_msg): stop(false) { |
||||
waiting_print.connect([this](){ |
||||
Singleton::terminal->async_print(line_nr-1, "."); |
||||
}); |
||||
start(start_msg); |
||||
} |
||||
|
||||
Terminal::InProgress::~InProgress() { |
||||
stop=true; |
||||
if(wait_thread.joinable()) |
||||
wait_thread.join(); |
||||
} |
||||
|
||||
void Terminal::InProgress::start(const std::string& msg) { |
||||
line_nr=Singleton::terminal->print(msg+"...\n"); |
||||
wait_thread=std::thread([this](){ |
||||
size_t c=0; |
||||
while(!stop) { |
||||
if(c%100==0) |
||||
waiting_print(); |
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10)); |
||||
c++; |
||||
} |
||||
}); |
||||
} |
||||
|
||||
void Terminal::InProgress::done(const std::string& msg) { |
||||
if(!stop) { |
||||
stop=true; |
||||
Singleton::terminal->async_print(line_nr-1, msg); |
||||
} |
||||
} |
||||
|
||||
void Terminal::InProgress::cancel(const std::string& msg) { |
||||
if(!stop) { |
||||
stop=true; |
||||
Singleton::terminal->async_print(line_nr-1, msg); |
||||
} |
||||
} |
||||
|
||||
Terminal::Terminal() { |
||||
bold_tag=get_buffer()->create_tag(); |
||||
bold_tag->property_weight()=PANGO_WEIGHT_BOLD; |
||||
|
||||
async_print_dispatcher.connect([this](){ |
||||
async_print_strings_mutex.lock(); |
||||
if(async_print_strings.size()>0) { |
||||
for(auto &string_bold: async_print_strings) |
||||
print(string_bold.first, string_bold.second); |
||||
async_print_strings.clear(); |
||||
} |
||||
async_print_strings_mutex.unlock(); |
||||
}); |
||||
async_print_on_line_dispatcher.connect([this](){ |
||||
async_print_on_line_strings_mutex.lock(); |
||||
if(async_print_on_line_strings.size()>0) { |
||||
for(auto &line_string: async_print_on_line_strings) |
||||
print(line_string.first, line_string.second); |
||||
async_print_on_line_strings.clear(); |
||||
} |
||||
async_print_on_line_strings_mutex.unlock(); |
||||
}); |
||||
} |
||||
|
||||
int Terminal::execute(const std::string &command, const boost::filesystem::path &path, bool use_pipes) { |
||||
int stdin_fd, stdout_fd, stderr_fd; |
||||
pid_t pid; |
||||
if(use_pipes) |
||||
pid=popen3(command, path.string(), &stdin_fd, &stdout_fd, &stderr_fd); |
||||
else |
||||
pid=popen3(command, path.string(), nullptr, nullptr, nullptr); |
||||
|
||||
if (pid<=0) { |
||||
async_print("Error: Failed to run command: " + command + "\n", true); |
||||
return -1; |
||||
} |
||||
else { |
||||
if(use_pipes) { |
||||
std::thread stderr_thread([this, stderr_fd](){ |
||||
char buffer[buffer_size]; |
||||
ssize_t n; |
||||
while ((n=read(stderr_fd, buffer, buffer_size)) > 0) { |
||||
std::string message; |
||||
message.reserve(n); |
||||
for(ssize_t c=0;c<n;c++) |
||||
message+=buffer[c]; |
||||
async_print(message, true); |
||||
} |
||||
}); |
||||
stderr_thread.detach(); |
||||
std::thread stdout_thread([this, stdout_fd](){ |
||||
char buffer[buffer_size]; |
||||
ssize_t n; |
||||
while ((n=read(stdout_fd, buffer, buffer_size)) > 0) { |
||||
std::string message; |
||||
message.reserve(n); |
||||
for(ssize_t c=0;c<n;c++) |
||||
message+=buffer[c]; |
||||
async_print(message); |
||||
} |
||||
}); |
||||
stdout_thread.detach(); |
||||
} |
||||
|
||||
int exit_code; |
||||
waitpid(pid, &exit_code, 0); |
||||
if(use_pipes) { |
||||
close(stdin_fd); |
||||
close(stdout_fd); |
||||
close(stderr_fd); |
||||
} |
||||
|
||||
return exit_code; |
||||
} |
||||
} |
||||
|
||||
int Terminal::execute(std::istream &stdin_stream, std::ostream &stdout_stream, const std::string &command, const boost::filesystem::path &path) { |
||||
int stdin_fd, stdout_fd, stderr_fd; |
||||
auto pid=popen3(command, path.string(), &stdin_fd, &stdout_fd, &stderr_fd); |
||||
|
||||
if (pid<=0) { |
||||
async_print("Error: Failed to run command: " + command + "\n", true); |
||||
return -1; |
||||
} |
||||
else { |
||||
std::thread stderr_thread([this, stderr_fd](){ |
||||
char buffer[buffer_size]; |
||||
ssize_t n; |
||||
while ((n=read(stderr_fd, buffer, buffer_size)) > 0) { |
||||
std::string message; |
||||
message.reserve(n); |
||||
for(ssize_t c=0;c<n;c++) |
||||
message+=buffer[c]; |
||||
async_print(message, true); |
||||
} |
||||
}); |
||||
stderr_thread.detach(); |
||||
std::thread stdout_thread([this, &stdout_stream, stdout_fd](){ |
||||
char buffer[buffer_size]; |
||||
ssize_t n; |
||||
while ((n=read(stdout_fd, buffer, buffer_size)) > 0) { |
||||
Glib::ustring umessage; |
||||
umessage.reserve(n); |
||||
for(ssize_t c=0;c<n;c++) |
||||
umessage+=buffer[c]; |
||||
Glib::ustring::iterator iter; |
||||
while(!umessage.validate(iter)) { |
||||
auto next_char_iter=iter; |
||||
next_char_iter++; |
||||
umessage.replace(iter, next_char_iter, "?"); |
||||
} |
||||
stdout_stream.write(umessage.data(), n); |
||||
} |
||||
}); |
||||
stdout_thread.detach(); |
||||
|
||||
char buffer[buffer_size]; |
||||
for(;;) { |
||||
stdin_stream.readsome(buffer, buffer_size); |
||||
auto read_n=stdin_stream.gcount(); |
||||
if(read_n==0) |
||||
break; |
||||
auto write_n=write(stdin_fd, buffer, read_n); |
||||
if(write_n==0) |
||||
break; |
||||
} |
||||
close(stdin_fd); |
||||
|
||||
int exit_code; |
||||
waitpid(pid, &exit_code, 0); |
||||
close(stdout_fd); |
||||
close(stderr_fd); |
||||
|
||||
return exit_code; |
||||
} |
||||
} |
||||
|
||||
void Terminal::async_execute(const std::string &command, const boost::filesystem::path &path, std::function<void(int exit_code)> callback) { |
||||
std::thread async_execute_thread([this, command, path, callback](){ |
||||
int stdin_fd, stdout_fd, stderr_fd; |
||||
async_executes_mutex.lock(); |
||||
stdin_buffer.clear(); |
||||
auto pid=popen3(command, path.string(), &stdin_fd, &stdout_fd, &stderr_fd); |
||||
async_executes.emplace_back(pid, stdin_fd); |
||||
async_executes_mutex.unlock(); |
||||
|
||||
if (pid<=0) { |
||||
async_print("Error: Failed to run command: " + command + "\n", true); |
||||
if(callback) |
||||
callback(-1); |
||||
} |
||||
else { |
||||
std::thread stderr_thread([this, stderr_fd](){ |
||||
char buffer[buffer_size]; |
||||
ssize_t n; |
||||
while ((n=read(stderr_fd, buffer, buffer_size)) > 0) { |
||||
std::string message; |
||||
message.reserve(n); |
||||
for(ssize_t c=0;c<n;c++) |
||||
message+=buffer[c]; |
||||
async_print(message, true); |
||||
} |
||||
}); |
||||
stderr_thread.detach(); |
||||
std::thread stdout_thread([this, stdout_fd](){ |
||||
char buffer[buffer_size]; |
||||
ssize_t n; |
||||
while ((n=read(stdout_fd, buffer, buffer_size)) > 0) { |
||||
std::string message; |
||||
message.reserve(n); |
||||
for(ssize_t c=0;c<n;c++) |
||||
message+=buffer[c]; |
||||
async_print(message); |
||||
} |
||||
}); |
||||
stdout_thread.detach(); |
||||
|
||||
int exit_code; |
||||
waitpid(pid, &exit_code, 0); |
||||
async_executes_mutex.lock(); |
||||
for(auto it=async_executes.begin();it!=async_executes.end();it++) { |
||||
if(it->first==pid) { |
||||
async_executes.erase(it); |
||||
break; |
||||
} |
||||
} |
||||
stdin_buffer.clear(); |
||||
close(stdin_fd); |
||||
close(stdout_fd); |
||||
close(stderr_fd); |
||||
async_executes_mutex.unlock(); |
||||
|
||||
if(callback) |
||||
callback(exit_code); |
||||
} |
||||
}); |
||||
async_execute_thread.detach(); |
||||
} |
||||
|
||||
void Terminal::kill_last_async_execute(bool force) { |
||||
async_executes_mutex.lock(); |
||||
if(async_executes.size()>0) { |
||||
if(force) |
||||
kill(-async_executes.back().first, SIGTERM); |
||||
else |
||||
kill(-async_executes.back().first, SIGINT); |
||||
} |
||||
async_executes_mutex.unlock(); |
||||
} |
||||
|
||||
void Terminal::kill_async_executes(bool force) { |
||||
async_executes_mutex.lock(); |
||||
for(auto &async_execute: async_executes) { |
||||
if(force) |
||||
kill(-async_execute.first, SIGTERM); |
||||
else |
||||
kill(-async_execute.first, SIGINT); |
||||
} |
||||
async_executes_mutex.unlock(); |
||||
} |
||||
|
||||
size_t Terminal::print(const std::string &message, bool bold){ |
||||
Glib::ustring umessage=message; |
||||
Glib::ustring::iterator iter; |
||||
while(!umessage.validate(iter)) { |
||||
auto next_char_iter=iter; |
||||
next_char_iter++; |
||||
umessage.replace(iter, next_char_iter, "?"); |
||||
} |
||||
|
||||
if(bold) |
||||
get_buffer()->insert_with_tag(get_buffer()->end(), umessage, bold_tag); |
||||
else |
||||
get_buffer()->insert(get_buffer()->end(), umessage); |
||||
|
||||
if(get_buffer()->get_line_count()>Singleton::config->terminal.history_size) { |
||||
int lines=get_buffer()->get_line_count()-Singleton::config->terminal.history_size; |
||||
get_buffer()->erase(get_buffer()->begin(), get_buffer()->get_iter_at_line(lines)); |
||||
deleted_lines+=static_cast<size_t>(lines); |
||||
} |
||||
|
||||
return static_cast<size_t>(get_buffer()->end().get_line())+deleted_lines; |
||||
} |
||||
|
||||
void Terminal::print(size_t line_nr, const std::string &message){ |
||||
if(line_nr<deleted_lines) |
||||
return; |
||||
|
||||
Glib::ustring umessage=message; |
||||
Glib::ustring::iterator iter; |
||||
while(!umessage.validate(iter)) { |
||||
auto next_char_iter=iter; |
||||
next_char_iter++; |
||||
umessage.replace(iter, next_char_iter, "?"); |
||||
} |
||||
|
||||
auto end_line_iter=get_buffer()->get_iter_at_line(static_cast<int>(line_nr-deleted_lines)); |
||||
while(!end_line_iter.ends_line() && end_line_iter.forward_char()) {} |
||||
get_buffer()->insert(end_line_iter, umessage); |
||||
} |
||||
|
||||
std::shared_ptr<Terminal::InProgress> Terminal::print_in_progress(std::string start_msg) { |
||||
std::shared_ptr<Terminal::InProgress> in_progress=std::shared_ptr<Terminal::InProgress>(new Terminal::InProgress(start_msg)); |
||||
return in_progress; |
||||
} |
||||
|
||||
void Terminal::async_print(const std::string &message, bool bold) { |
||||
async_print_strings_mutex.lock(); |
||||
bool dispatch=true; |
||||
if(async_print_strings.size()>0) |
||||
dispatch=false; |
||||
async_print_strings.emplace_back(message, bold); |
||||
async_print_strings_mutex.unlock(); |
||||
if(dispatch) |
||||
async_print_dispatcher(); |
||||
} |
||||
|
||||
void Terminal::async_print(int line_nr, const std::string &message) { |
||||
async_print_on_line_strings_mutex.lock(); |
||||
bool dispatch=true; |
||||
if(async_print_on_line_strings.size()>0) |
||||
dispatch=false; |
||||
async_print_on_line_strings.emplace_back(line_nr, message); |
||||
async_print_on_line_strings_mutex.unlock(); |
||||
if(dispatch) |
||||
async_print_on_line_dispatcher(); |
||||
} |
||||
|
||||
bool Terminal::on_key_press_event(GdkEventKey *event) { |
||||
async_executes_mutex.lock(); |
||||
if(async_executes.size()>0) { |
||||
get_buffer()->place_cursor(get_buffer()->end()); |
||||
auto unicode=gdk_keyval_to_unicode(event->keyval); |
||||
char chr=(char)unicode; |
||||
if(unicode>=32 && unicode<=126) { |
||||
stdin_buffer+=chr; |
||||
get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1)); |
||||
} |
||||
else if(event->keyval==GDK_KEY_BackSpace) { |
||||
if(stdin_buffer.size()>0 && get_buffer()->get_char_count()>0) { |
||||
auto iter=get_buffer()->end(); |
||||
iter--; |
||||
stdin_buffer.pop_back(); |
||||
get_buffer()->erase(iter, get_buffer()->end()); |
||||
} |
||||
} |
||||
else if(event->keyval==GDK_KEY_Return) { |
||||
stdin_buffer+='\n'; |
||||
write(async_executes.back().second, stdin_buffer.c_str(), stdin_buffer.size()); |
||||
get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1)); |
||||
stdin_buffer.clear(); |
||||
} |
||||
} |
||||
async_executes_mutex.unlock(); |
||||
return true; |
||||
} |
||||
@ -1,512 +0,0 @@
|
||||
#include "terminal.h" |
||||
#include <iostream> |
||||
#include "logging.h" |
||||
#include "singletons.h" |
||||
#include <unistd.h> |
||||
#include <windows.h> |
||||
|
||||
#include <iostream> //TODO: remove |
||||
using namespace std; //TODO: remove
|
||||
|
||||
const size_t buffer_size=131072; |
||||
|
||||
//Based on the example at https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx
|
||||
//Note: on Windows it seems impossible to specify which pipes to use
|
||||
//Thus, if stdin_h, stdout_h and stderr all are NULL, the out,err,in is sent to the parent process instead
|
||||
HANDLE popen3(const std::string &command, const std::string &path, HANDLE *stdin_h, HANDLE *stdout_h, HANDLE *stderr_h) { |
||||
HANDLE g_hChildStd_IN_Rd = NULL; |
||||
HANDLE g_hChildStd_IN_Wr = NULL; |
||||
HANDLE g_hChildStd_OUT_Rd = NULL; |
||||
HANDLE g_hChildStd_OUT_Wr = NULL; |
||||
HANDLE g_hChildStd_ERR_Rd = NULL; |
||||
HANDLE g_hChildStd_ERR_Wr = NULL; |
||||
|
||||
SECURITY_ATTRIBUTES saAttr; |
||||
|
||||
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
saAttr.bInheritHandle = TRUE; |
||||
saAttr.lpSecurityDescriptor = NULL; |
||||
|
||||
if(stdin_h!=nullptr) { |
||||
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
|
||||
return NULL; |
||||
if(!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) { |
||||
CloseHandle(g_hChildStd_IN_Rd); |
||||
CloseHandle(g_hChildStd_IN_Wr); |
||||
return NULL; |
||||
} |
||||
} |
||||
if(stdout_h!=nullptr) { |
||||
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) { |
||||
if(stdin_h!=nullptr) CloseHandle(g_hChildStd_IN_Rd); |
||||
if(stdin_h!=nullptr) CloseHandle(g_hChildStd_IN_Wr); |
||||
return NULL; |
||||
} |
||||
if(!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) { |
||||
if(stdin_h!=nullptr) CloseHandle(g_hChildStd_IN_Rd); |
||||
if(stdin_h!=nullptr) CloseHandle(g_hChildStd_IN_Wr); |
||||
CloseHandle(g_hChildStd_OUT_Rd); |
||||
CloseHandle(g_hChildStd_OUT_Wr); |
||||
return NULL; |
||||
} |
||||
} |
||||
if(stderr_h!=nullptr) { |
||||
if (!CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr, 0)) { |
||||
if(stdin_h!=nullptr) CloseHandle(g_hChildStd_IN_Rd); |
||||
if(stdin_h!=nullptr) CloseHandle(g_hChildStd_IN_Wr); |
||||
if(stdout_h!=nullptr) CloseHandle(g_hChildStd_OUT_Rd); |
||||
if(stdout_h!=nullptr) CloseHandle(g_hChildStd_OUT_Wr); |
||||
return NULL; |
||||
} |
||||
if(!SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0)) { |
||||
if(stdin_h!=nullptr) CloseHandle(g_hChildStd_IN_Rd); |
||||
if(stdin_h!=nullptr) CloseHandle(g_hChildStd_IN_Wr); |
||||
if(stdout_h!=nullptr) CloseHandle(g_hChildStd_OUT_Rd); |
||||
if(stdout_h!=nullptr) CloseHandle(g_hChildStd_OUT_Wr); |
||||
CloseHandle(g_hChildStd_ERR_Rd); |
||||
CloseHandle(g_hChildStd_ERR_Wr); |
||||
return NULL; |
||||
} |
||||
} |
||||
|
||||
PROCESS_INFORMATION process_info;
|
||||
STARTUPINFO siStartInfo; |
||||
|
||||
ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION)); |
||||
|
||||
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); |
||||
siStartInfo.cb = sizeof(STARTUPINFO); |
||||
if(stdin_h!=nullptr) siStartInfo.hStdInput = g_hChildStd_IN_Rd; |
||||
if(stdout_h!=nullptr) siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; |
||||
if(stderr_h!=nullptr) siStartInfo.hStdError = g_hChildStd_ERR_Wr; |
||||
if(stdin_h!=nullptr || stdout_h!=nullptr || stderr_h!=nullptr) |
||||
siStartInfo.dwFlags |= STARTF_USESTDHANDLES; |
||||
|
||||
char* path_ptr; |
||||
if(path=="") |
||||
path_ptr=NULL; |
||||
else { |
||||
path_ptr=new char[path.size()+1]; |
||||
std::strcpy(path_ptr, path.c_str()); |
||||
} |
||||
char* command_cstr=new char[command.size()+1]; |
||||
std::strcpy(command_cstr, command.c_str()); |
||||
BOOL bSuccess = CreateProcess(NULL,
|
||||
command_cstr, // command line
|
||||
NULL, // process security attributes
|
||||
NULL, // primary thread security attributes
|
||||
TRUE, // handles are inherited
|
||||
0, // creation flags
|
||||
NULL, // use parent's environment
|
||||
path_ptr, // use parent's current directory
|
||||
&siStartInfo, // STARTUPINFO pointer
|
||||
&process_info); // receives PROCESS_INFORMATION
|
||||
|
||||
if(!bSuccess) { |
||||
CloseHandle(process_info.hProcess); |
||||
CloseHandle(process_info.hThread); |
||||
if(stdin_h!=nullptr) CloseHandle(g_hChildStd_IN_Rd); |
||||
if(stdout_h!=nullptr) CloseHandle(g_hChildStd_OUT_Wr); |
||||
if(stderr_h!=nullptr) CloseHandle(g_hChildStd_ERR_Wr); |
||||
return NULL; |
||||
} |
||||
else { |
||||
// Close handles to the child process and its primary thread.
|
||||
// Some applications might keep these handles to monitor the status
|
||||
// of the child process, for example.
|
||||
|
||||
CloseHandle(process_info.hThread); |
||||
if(stdin_h!=nullptr) CloseHandle(g_hChildStd_IN_Rd); |
||||
if(stdout_h!=nullptr) CloseHandle(g_hChildStd_OUT_Wr); |
||||
if(stderr_h!=nullptr) CloseHandle(g_hChildStd_ERR_Wr); |
||||
} |
||||
|
||||
if(stdin_h!=NULL) *stdin_h=g_hChildStd_IN_Wr; |
||||
if(stdout_h!=NULL) *stdout_h=g_hChildStd_OUT_Rd; |
||||
if(stderr_h!=NULL) *stderr_h=g_hChildStd_ERR_Rd; |
||||
return process_info.hProcess; |
||||
} |
||||
|
||||
Terminal::InProgress::InProgress(const std::string& start_msg): stop(false) { |
||||
waiting_print.connect([this](){ |
||||
Singleton::terminal->async_print(line_nr-1, "."); |
||||
}); |
||||
start(start_msg); |
||||
} |
||||
|
||||
Terminal::InProgress::~InProgress() { |
||||
stop=true; |
||||
if(wait_thread.joinable()) |
||||
wait_thread.join(); |
||||
} |
||||
|
||||
void Terminal::InProgress::start(const std::string& msg) { |
||||
line_nr=Singleton::terminal->print(msg+"...\n"); |
||||
wait_thread=std::thread([this](){ |
||||
size_t c=0; |
||||
while(!stop) { |
||||
if(c%100==0) |
||||
waiting_print(); |
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10)); |
||||
c++; |
||||
} |
||||
}); |
||||
} |
||||
|
||||
void Terminal::InProgress::done(const std::string& msg) { |
||||
if(!stop) { |
||||
stop=true; |
||||
Singleton::terminal->async_print(line_nr-1, msg); |
||||
} |
||||
} |
||||
|
||||
void Terminal::InProgress::cancel(const std::string& msg) { |
||||
if(!stop) { |
||||
stop=true; |
||||
Singleton::terminal->async_print(line_nr-1, msg); |
||||
} |
||||
} |
||||
|
||||
Terminal::Terminal() { |
||||
bold_tag=get_buffer()->create_tag(); |
||||
bold_tag->property_weight()=PANGO_WEIGHT_BOLD; |
||||
|
||||
async_print_dispatcher.connect([this](){ |
||||
async_print_strings_mutex.lock(); |
||||
if(async_print_strings.size()>0) { |
||||
for(auto &string_bold: async_print_strings) |
||||
print(string_bold.first, string_bold.second); |
||||
async_print_strings.clear(); |
||||
} |
||||
async_print_strings_mutex.unlock(); |
||||
}); |
||||
async_print_on_line_dispatcher.connect([this](){ |
||||
async_print_on_line_strings_mutex.lock(); |
||||
if(async_print_on_line_strings.size()>0) { |
||||
for(auto &line_string: async_print_on_line_strings) |
||||
print(line_string.first, line_string.second); |
||||
async_print_on_line_strings.clear(); |
||||
} |
||||
async_print_on_line_strings_mutex.unlock(); |
||||
}); |
||||
} |
||||
|
||||
int Terminal::execute(const std::string &command, const boost::filesystem::path &path, bool use_pipes) { |
||||
HANDLE stdin_h, stdout_h, stderr_h; |
||||
|
||||
HANDLE process; |
||||
if(use_pipes) |
||||
process=popen3(command, path.string(), &stdin_h, &stdout_h, &stderr_h); |
||||
else |
||||
process=popen3(command, path.string(), nullptr, nullptr, nullptr); |
||||
if(process==NULL) { |
||||
async_print("Error: Failed to run command: " + command + "\n", true); |
||||
return -1; |
||||
} |
||||
if(use_pipes) { |
||||
std::thread stderr_thread([this, stderr_h](){ |
||||
DWORD n; |
||||
CHAR buffer[buffer_size]; |
||||
for (;;) { |
||||
BOOL bSuccess = ReadFile(stderr_h, buffer, static_cast<DWORD>(buffer_size), &n, NULL); |
||||
if(!bSuccess || n == 0) |
||||
break; |
||||
|
||||
std::string message; |
||||
message.reserve(n); |
||||
for(DWORD c=0;c<n;c++) |
||||
message+=buffer[c]; |
||||
async_print(message, true); |
||||
} |
||||
}); |
||||
stderr_thread.detach(); |
||||
|
||||
std::thread stdout_thread([this, stdout_h](){ |
||||
DWORD n; |
||||
CHAR buffer[buffer_size]; |
||||
for (;;) { |
||||
BOOL bSuccess = ReadFile(stdout_h, buffer, static_cast<DWORD>(buffer_size), &n, NULL); |
||||
if(!bSuccess || n == 0) |
||||
break; |
||||
|
||||
std::string message; |
||||
message.reserve(n); |
||||
for(DWORD c=0;c<n;c++) |
||||
message+=buffer[c]; |
||||
async_print(message); |
||||
} |
||||
}); |
||||
stdout_thread.detach(); |
||||
} |
||||
|
||||
unsigned long exit_code; |
||||
WaitForSingleObject(process, INFINITE); |
||||
GetExitCodeProcess(process, &exit_code); |
||||
|
||||
CloseHandle(process); |
||||
if(use_pipes) { |
||||
CloseHandle(stdin_h); |
||||
CloseHandle(stdout_h); |
||||
CloseHandle(stderr_h); |
||||
} |
||||
return exit_code; |
||||
} |
||||
|
||||
int Terminal::execute(std::istream &stdin_stream, std::ostream &stdout_stream, const std::string &command, const boost::filesystem::path &path) { |
||||
HANDLE stdin_h, stdout_h, stderr_h; |
||||
|
||||
auto process=popen3(command, path.string(), &stdin_h, &stdout_h, &stderr_h); |
||||
if(process==NULL) { |
||||
async_print("Error: Failed to run command: " + command + "\n", true); |
||||
return -1; |
||||
} |
||||
std::thread stderr_thread([this, stderr_h](){ |
||||
DWORD n; |
||||
CHAR buffer[buffer_size]; |
||||
for (;;) { |
||||
BOOL bSuccess = ReadFile(stderr_h, buffer, static_cast<DWORD>(buffer_size), &n, NULL); |
||||
if(!bSuccess || n == 0) |
||||
break; |
||||
|
||||
std::string message; |
||||
message.reserve(n); |
||||
for(DWORD c=0;c<n;c++) |
||||
message+=buffer[c]; |
||||
async_print(message, true); |
||||
} |
||||
}); |
||||
stderr_thread.detach(); |
||||
|
||||
std::thread stdout_thread([this, &stdout_stream, stdout_h](){ |
||||
DWORD n; |
||||
CHAR buffer[buffer_size]; |
||||
for (;;) { |
||||
BOOL bSuccess = ReadFile(stdout_h, buffer, static_cast<DWORD>(buffer_size), &n, NULL); |
||||
if(!bSuccess || n == 0) |
||||
break; |
||||
Glib::ustring umessage; |
||||
umessage.reserve(n); |
||||
for(DWORD c=0;c<n;c++) |
||||
umessage+=buffer[c]; |
||||
Glib::ustring::iterator iter; |
||||
while(!umessage.validate(iter)) { |
||||
auto next_char_iter=iter; |
||||
next_char_iter++; |
||||
umessage.replace(iter, next_char_iter, "?"); |
||||
} |
||||
stdout_stream.write(umessage.data(), static_cast<ssize_t>(n)); |
||||
} |
||||
}); |
||||
stdout_thread.detach(); |
||||
|
||||
CHAR buffer[buffer_size]; |
||||
for(;;) { |
||||
stdin_stream.readsome(buffer, buffer_size); |
||||
auto read_n=stdin_stream.gcount(); |
||||
if(read_n==0) |
||||
break; |
||||
DWORD write_n; |
||||
BOOL bSuccess = WriteFile(stdin_h, buffer, static_cast<DWORD>(read_n), &write_n, NULL); |
||||
if(!bSuccess || write_n==0) |
||||
break; |
||||
} |
||||
CloseHandle(stdin_h); |
||||
|
||||
unsigned long exit_code; |
||||
WaitForSingleObject(process, INFINITE); |
||||
GetExitCodeProcess(process, &exit_code); |
||||
|
||||
CloseHandle(process); |
||||
CloseHandle(stdout_h); |
||||
CloseHandle(stderr_h); |
||||
return exit_code; |
||||
} |
||||
|
||||
void Terminal::async_execute(const std::string &command, const boost::filesystem::path &path, std::function<void(int exit_code)> callback) { |
||||
std::thread async_execute_thread([this, command, path, callback](){ |
||||
HANDLE stdin_h, stdout_h, stderr_h; |
||||
|
||||
async_executes_mutex.lock(); |
||||
stdin_buffer.clear(); |
||||
auto process=popen3(command, path.string(), &stdin_h, &stdout_h, &stderr_h); |
||||
if(process==NULL) { |
||||
async_executes_mutex.unlock(); |
||||
async_print("Error: Failed to run command: " + command + "\n", true); |
||||
if(callback) |
||||
callback(-1); |
||||
return; |
||||
} |
||||
async_executes.emplace_back(process, stdin_h); |
||||
async_executes_mutex.unlock(); |
||||
|
||||
std::thread stderr_thread([this, stderr_h](){ |
||||
DWORD n; |
||||
CHAR buffer[buffer_size]; |
||||
for (;;) { |
||||
BOOL bSuccess = ReadFile(stderr_h, buffer, static_cast<DWORD>(buffer_size), &n, NULL); |
||||
if(!bSuccess || n == 0) |
||||
break; |
||||
|
||||
std::string message; |
||||
message.reserve(n); |
||||
for(DWORD c=0;c<n;c++) |
||||
message+=buffer[c]; |
||||
async_print(message, true); |
||||
} |
||||
}); |
||||
stderr_thread.detach(); |
||||
|
||||
std::thread stdout_thread([this, stdout_h](){ |
||||
DWORD n; |
||||
CHAR buffer[buffer_size]; |
||||
for (;;) { |
||||
BOOL bSuccess = ReadFile(stdout_h, buffer, static_cast<DWORD>(buffer_size), &n, NULL); |
||||
if(!bSuccess || n == 0) |
||||
break; |
||||
|
||||
std::string message; |
||||
message.reserve(n); |
||||
for(DWORD c=0;c<n;c++) |
||||
message+=buffer[c]; |
||||
async_print(message); |
||||
} |
||||
}); |
||||
stdout_thread.detach(); |
||||
|
||||
unsigned long exit_code; |
||||
WaitForSingleObject(process, INFINITE); |
||||
GetExitCodeProcess(process, &exit_code); |
||||
|
||||
async_executes_mutex.lock(); |
||||
for(auto it=async_executes.begin();it!=async_executes.end();it++) { |
||||
if(it->first==process) { |
||||
async_executes.erase(it); |
||||
break; |
||||
} |
||||
} |
||||
stdin_buffer.clear(); |
||||
CloseHandle(process); |
||||
CloseHandle(stdin_h); |
||||
CloseHandle(stdout_h); |
||||
CloseHandle(stderr_h); |
||||
async_executes_mutex.unlock(); |
||||
|
||||
if(callback) |
||||
callback(exit_code); |
||||
}); |
||||
async_execute_thread.detach(); |
||||
} |
||||
|
||||
void Terminal::kill_last_async_execute(bool force) { |
||||
async_executes_mutex.lock(); |
||||
if(async_executes.size()>0) { |
||||
TerminateProcess(async_executes.back().first, 2); |
||||
} |
||||
async_executes_mutex.unlock(); |
||||
} |
||||
|
||||
void Terminal::kill_async_executes(bool force) { |
||||
async_executes_mutex.lock(); |
||||
for(auto &async_execute: async_executes) { |
||||
TerminateProcess(async_execute.first, 2); |
||||
} |
||||
async_executes_mutex.unlock(); |
||||
} |
||||
|
||||
size_t Terminal::print(const std::string &message, bool bold){ |
||||
Glib::ustring umessage=message; |
||||
Glib::ustring::iterator iter; |
||||
while(!umessage.validate(iter)) { |
||||
auto next_char_iter=iter; |
||||
next_char_iter++; |
||||
umessage.replace(iter, next_char_iter, "?"); |
||||
} |
||||
|
||||
if(bold) |
||||
get_buffer()->insert_with_tag(get_buffer()->end(), umessage, bold_tag); |
||||
else |
||||
get_buffer()->insert(get_buffer()->end(), umessage); |
||||
|
||||
if(get_buffer()->get_line_count()>Singleton::config->terminal.history_size) { |
||||
int lines=get_buffer()->get_line_count()-Singleton::config->terminal.history_size; |
||||
get_buffer()->erase(get_buffer()->begin(), get_buffer()->get_iter_at_line(lines)); |
||||
deleted_lines+=static_cast<size_t>(lines); |
||||
} |
||||
|
||||
return static_cast<size_t>(get_buffer()->end().get_line())+deleted_lines; |
||||
} |
||||
|
||||
void Terminal::print(size_t line_nr, const std::string &message){ |
||||
if(line_nr<deleted_lines) |
||||
return; |
||||
|
||||
Glib::ustring umessage=message; |
||||
Glib::ustring::iterator iter; |
||||
while(!umessage.validate(iter)) { |
||||
auto next_char_iter=iter; |
||||
next_char_iter++; |
||||
umessage.replace(iter, next_char_iter, "?"); |
||||
} |
||||
|
||||
auto end_line_iter=get_buffer()->get_iter_at_line(static_cast<int>(line_nr-deleted_lines)); |
||||
while(!end_line_iter.ends_line() && end_line_iter.forward_char()) {} |
||||
get_buffer()->insert(end_line_iter, umessage); |
||||
} |
||||
|
||||
std::shared_ptr<Terminal::InProgress> Terminal::print_in_progress(std::string start_msg) { |
||||
std::shared_ptr<Terminal::InProgress> in_progress=std::shared_ptr<Terminal::InProgress>(new Terminal::InProgress(start_msg)); |
||||
return in_progress; |
||||
} |
||||
|
||||
void Terminal::async_print(const std::string &message, bool bold) { |
||||
async_print_strings_mutex.lock(); |
||||
bool dispatch=true; |
||||
if(async_print_strings.size()>0) |
||||
dispatch=false; |
||||
async_print_strings.emplace_back(message, bold); |
||||
async_print_strings_mutex.unlock(); |
||||
if(dispatch) |
||||
async_print_dispatcher(); |
||||
} |
||||
|
||||
void Terminal::async_print(int line_nr, const std::string &message) { |
||||
async_print_on_line_strings_mutex.lock(); |
||||
bool dispatch=true; |
||||
if(async_print_on_line_strings.size()>0) |
||||
dispatch=false; |
||||
async_print_on_line_strings.emplace_back(line_nr, message); |
||||
async_print_on_line_strings_mutex.unlock(); |
||||
if(dispatch) |
||||
async_print_on_line_dispatcher(); |
||||
} |
||||
|
||||
bool Terminal::on_key_press_event(GdkEventKey *event) { |
||||
async_executes_mutex.lock(); |
||||
if(async_executes.size()>0) { |
||||
get_buffer()->place_cursor(get_buffer()->end()); |
||||
auto unicode=gdk_keyval_to_unicode(event->keyval); |
||||
char chr=(char)unicode; |
||||
if(unicode>=32 && unicode<=126) { |
||||
stdin_buffer+=chr; |
||||
get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1)); |
||||
} |
||||
else if(event->keyval==GDK_KEY_BackSpace) { |
||||
if(stdin_buffer.size()>0 && get_buffer()->get_char_count()>0) { |
||||
auto iter=get_buffer()->end(); |
||||
iter--; |
||||
stdin_buffer.pop_back(); |
||||
get_buffer()->erase(iter, get_buffer()->end()); |
||||
} |
||||
} |
||||
else if(event->keyval==GDK_KEY_Return) { |
||||
stdin_buffer+='\n'; |
||||
DWORD written; |
||||
WriteFile(async_executes.back().second, stdin_buffer.c_str(), stdin_buffer.size(), &written, NULL); |
||||
//TODO: is this line needed?
|
||||
get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1)); |
||||
stdin_buffer.clear(); |
||||
} |
||||
} |
||||
async_executes_mutex.unlock(); |
||||
return true; |
||||
} |
||||
Loading…
Reference in new issue