Commit 15e91361 authored by Arnaud Blanchard's avatar Arnaud Blanchard

Create gtk_channels projec to have a gtk interface managing channels

parent c2172c87
//
// Channel.cpp
// e_bash
//
// Created by Arnaud Blanchard on 29/09/2019.
//
#include "Channel.hpp"
#include "blc_channel.h"
#include "string"
#include <vector>
#include <vte/vte.h>
#include <thread>
using namespace Gtk;
using namespace std;
struct Process{
pid_t pid;
char **argv;
string name;
string program_name;
string channel;
void run();
void watch();
};
void Process::watch(){
char pipe_buffer[PIPE_SIZE];
ssize_t pipe_read_nb=0, pipe_buffer_writen_nb=0;
int fd_max, status;
ssize_t size=-1, written_nb, read_nb;
fd_set fd_read_set, fd_write_set;
fd_max=MAX(stdout_fileno, stderr_fileno);
// if (input_process) fd_max=MAX(fd_max, input_process->stdout_fileno);
// fd_max=MAX(fd_max, stdin_fileno);
do
{
size=0;
FD_ZERO(&fd_read_set);
// FD_ZERO(&fd_write_set);
//if (input_process) FD_SET(input_process->stdout_fileno, &fd_read_set);
FD_SET(stdout_fileno, &fd_read_set);
FD_SET(stderr_fileno, &fd_read_set);
// if (stdin_fileno!=-1 && pipe_read_nb) FD_SET(stdin_fileno, &fd_write_set);
SYSTEM_ERROR_CHECK(::select(fd_max+1, &fd_read_set, NULL, NULL, NULL), -1, "process: '%s' '%s'", name, program->name);
/* if (stdin_fileno!=-1 && pipe_read_nb && FD_ISSET(stdin_fileno, &fd_write_set)){ //something to write and input ready;
SYSTEM_ERROR_CHECK(written_nb=write(stdin_fileno, &pipe_buffer[pipe_buffer_writen_nb], pipe_read_nb-pipe_buffer_writen_nb),-1, "pipe_buffer_writen_nb '%ld', pipe_read_nb '%ld'", pipe_buffer_writen_nb, pipe_read_nb);
SYSTEM_ERROR_CHECK(written_nb=write(STDERR_FILENO, &pipe_buffer[pipe_buffer_writen_nb], pipe_read_nb-pipe_buffer_writen_nb),-1, "pipe_buffer_writen_nb '%ld', pipe_read_nb '%ld'", pipe_buffer_writen_nb, pipe_read_nb);
pipe_buffer_writen_nb+=written_nb;
size+=written_nb;
}
if (input_process && FD_ISSET(input_process->stdout_fileno, &fd_read_set)){
SYSTEM_ERROR_CHECK(read_nb=read(input_process->stdout_fileno, &pipe_buffer[pipe_read_nb], sizeof(pipe_buffer)-pipe_read_nb), -1, "input pipe");
pipe_read_nb+=read_nb;
size+=read_nb;
}
if (pipe_read_nb && pipe_buffer_writen_nb==pipe_read_nb){ //Everything has been written
pipe_buffer_writen_nb=0;
pipe_read_nb=0;
}*/
if (FD_ISSET(stdout_fileno, &fd_read_set)){
SYSTEM_ERROR_CHECK(size=read(stdout_fileno, pipe_buffer, sizeof(pipe_buffer)), -1, "stdout");
if (output_process) SYSTEM_ERROR_CHECK(write(next_process_pipe[1], pipe_buffer, size), -1, "next_process_pipe");
buffer_to_vte_terminal(pipe_buffer, size, stdout_terminal);
}
if (FD_ISSET(stderr_fileno, &fd_read_set)){
SYSTEM_ERROR_CHECK(size=read(stderr_fileno, pipe_buffer, sizeof(pipe_buffer)), -1, "stderr");
buffer_to_vte_terminal(pipe_buffer, size, stderr_terminal);
}
}
while(size!=0);
close(stdout_fileno);
close(stderr_fileno);
waitpid(pid, &status, 0);
pid=-1;
g_signal_handler_disconnect(G_OBJECT(stderr_terminal), commit_handler);
// if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(err_toggle_button))) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(err_toggle_button), false);
gdk_threads_add_idle (thread_safe_end_process, this);
}
void Process_old::run(){
int stdin_pipe[2], stderr_pipe[2], stdout_pipe[2];
SYSTEM_ERROR_CHECK(pipe(stdin_pipe), -1, NULL);
SYSTEM_ERROR_CHECK(pipe(stdout_pipe), -1, NULL);
SYSTEM_ERROR_CHECK(pipe(stderr_pipe), -1, NULL);
pid=fork();
if (pid==0){
/*Connection to virtual termainals*/
SYSTEM_ERROR_RETRY_ON_SPECIFIC_ERRNO(dup2(stdin_pipe[0] , STDIN_FILENO ), -1, EINTR, NULL);
SYSTEM_ERROR_RETRY_ON_SPECIFIC_ERRNO(dup2(stdout_pipe[1], STDOUT_FILENO), -1, EINTR, NULL);
SYSTEM_ERROR_RETRY_ON_SPECIFIC_ERRNO(dup2(stderr_pipe[1], STDERR_FILENO), -1, EINTR, NULL);
blc_close_pipe(stdin_pipe);
blc_close_pipe(stdout_pipe);
blc_close_pipe(stderr_pipe);
SYSTEM_ERROR_CHECK(execvp(program_name.data(), argv), -1, "Executing program '%s'", program_name.data());
}
close(stdin_pipe[0]);
close(stdout_pipe[1]);
close(stderr_pipe[1]);
thread thread([](){
watch();
});
//commit_handler=g_signal_connect(G_OBJECT(stderr_terminal), "commit", G_CALLBACK(input_cp), this);
}
struct ModelColumns : public TreeModel::ColumnRecord{
Gtk::TreeModelColumn<string> name, type, format, dimensions, icon;
ModelColumns(){
add(name);
add(type);
add(format);
add(dimensions);
add(icon);
}
void add_channel( Glib::RefPtr<Gtk::ListStore> &list, blc_channel *channel){
uint32_t type_str, format_str;
char dims_str[256];
auto row = *(list->append()); //* is because it is an iterator and we want the content of the ref.
row[name] = channel->name;
row[type] = string(UINT32_TO_STRING(type_str, channel->type), 4);
row[format] = string(UINT32_TO_STRING(format_str, channel->format), 4);
channel->sprint_dims(dims_str, 256);
row[dimensions] = dims_str;
row[icon] = Gtk::Stock::YES.id; //dialog-information";
}
}m_Columns;
static void refresh_channels(void* user_data){
auto channel_widget = (ChannelWidget *)user_data;
Glib::signal_idle().connect([&channel_widget](){ //important to use an idle otherwise Thread problems
channel_widget->refresh_channel_list();
return false;
});
}
ChannelWidget::ChannelWidget(){
Gtk::CellRendererPixbuf cellRendererPixBuf;
list_store = Gtk::ListStore::create(m_Columns);
set_model(list_store);
append_column("Name", m_Columns.name);
append_column("Type", m_Columns.type);
append_column("Format", m_Columns.format);
append_column("Dimensions", m_Columns.dimensions);
int numcols = append_column("image ", cellRendererPixBuf);
get_column(numcols-1)->add_attribute(cellRendererPixBuf, "stock-id", m_Columns.icon);
refresh_channel_list();
blc_channel_check_for_event(refresh_channels, this);
show_all_children();
signal_row_activated().connect([this](const TreeModel::Path& path, TreeViewColumn* column){
vector <char*> argv_vec;
Process *process = new Process();
Gtk::TreeIter iter=this->list_store->get_iter(path);
Gtk::TreeModel::Row row = *iter;
process->program_name="o_gnuplot";
process->name=process->program_name;
process->channel=((string)row[m_Columns.name]).data();
fprintf(stderr, "Row %s\n", ((string)row[m_Columns.name]).data());
argv_vec.emplace_back((char*)process->program_name.data());
argv_vec.emplace_back((char*)process->channel.data());
argv_vec.emplace_back(nullptr);
process->argv=argv_vec.data();
process->run();
return false;
});
signal_button_release_event().connect([](GdkEventButton* event){
if (event->button == 3){
exit(1);
};
return false;
});
}
void ChannelWidget::refresh_channel_list(){
int channels_nb;
blc_channel *channel_infos=nullptr;
channels_nb = blc_channel_get_all_infos(&channel_infos);
list_store->clear();
for(blc_channel *channel=channel_infos; channel != channel_infos+channels_nb; channel++){
m_Columns.add_channel(list_store, channel);
}
free(channel_infos);
}
......@@ -10,16 +10,10 @@
#include <gtkmm.h>
struct ChannelWidget:Gtk::TreeView{
std::string blaar_dir;
Glib::RefPtr<Gtk::TreeStore> tree_store;
Glib::RefPtr<Gtk::ListStore> list_store;
ChannelWidget();
void add_process(Gtk::TreeModel::Row &row);
void display_process(Gtk::TreeModel::Row &row);
void refresh_channel_list();
};
#endif /* Channel_hpp */
//
// Plot.cpp
// e_bash
//
// Created by Arnaud Blanchard on 30/09/2019.
//
#include "Plot.hpp"
Plot::Plot(){
}
//
// Plot.hpp
// e_bash
//
// Created by Arnaud Blanchard on 30/09/2019.
//
#ifndef Plot_hpp
#define Plot_hpp
#include <stdio.h>
#include <gtkmm-plplot.h>
struct Plot{
Gtk::PLplot::Plot2D plot;
Gtk::PLplot::Canvas canvas(plot);
Plot();
// plot.
};
#endif /* Plot_hpp */
......@@ -4,6 +4,7 @@ cmake_minimum_required(VERSION 2.6)
project(gtk_channels)
find_package(blc_channel REQUIRED)
find_package(blc_image REQUIRED)
find_package(blc_program REQUIRED)
find_package(blc_process REQUIRED)
......@@ -17,5 +18,5 @@ pkg_check_modules(VTE3 vte-2.91 REQUIRED) #libvtemm is not yet common
add_definitions(-std=c++14 ${BL_DEFINITIONS} ${GTK3_CFLAGS_OTHER} )
include_directories(${BL_INCLUDE_DIRS} ${GTK3_INCLUDE_DIRS} ${GTK3_SOURCE_VIEW_INCLUDE_DIRS} ${VTE_INCLUDE_DIRS} ${GTKMM_INCLUDE_DIRS} ${VTE3_INCLUDE_DIRS})
link_directories(${GTK3_LIBRARY_DIRS} ${VTE_LIBRARY_DIRS} /usr/local/lib)
add_executable(gtk_channels gtk_channels.cpp Channel.cpp Process.cpp parse_help.cpp)
add_executable(gtk_channels gtk_channels.cpp ChannelWidget.cpp ContextMenu.cpp ChannelColumns.cpp Process.cpp parse_help.cpp)
target_link_libraries(gtk_channels ${BL_LIBRARIES} ${GTK3_LIBRARIES} ${GTK3_SOURCE_VIEW_LIBRARIES} ${VTE_LIBRARIES} ${GTKMM_LIBRARIES} ${VTE3_LIBRARIES})
//
// Channel.cpp
// e_bash
//
// Created by Arnaud Blanchard on 29/09/2019.
//
#include "Process.h"
#include "Channel.hpp"
#include "blc_channel.h"
#include "string"
#include <vector>
#include <forward_list>
#include <vte/vte.h>
#include <thread>
#include <iostream>
using namespace Gtk;
using namespace Glib; //signal_idle,RefPtr
using namespace std;
vector <char*> argv_vec;
struct ChannelMenu:Gtk::Menu{
ChannelMenu(Gtk::TreeView &tree_view);
};
struct ModelColumns : public TreeModel::ColumnRecord{
TreeModelColumn<int> id, pid;
TreeModelColumn<string> name, type, format, dimensions, icon;
TreeModelColumn<Process*> process;
ModelColumns(){
add(id);
add(name);
add(type);
add(format);
add(dimensions);
add(icon);
add(pid);
add(process);
}
void add_channel(RefPtr<TreeStore> &tree, blc_channel *channel){
uint32_t type_str, format_str;
char dims_str[256];
TreeModel::Row childrow;
auto row = *(tree->append()); //* is because it is an iterator and we want the content of the ref.
//row[id] = channel->id;
row[name] = channel->name;
row[type] = string(UINT32_TO_STRING(type_str, channel->type), 4);
row[format] = string(UINT32_TO_STRING(format_str, channel->format), 4);
channel->sprint_dims(dims_str, 256);
row[dimensions] = dims_str;
row[process]=nullptr;
row[id]=channel->id;
// row[icon] = Gtk::Stock::YES.id; //dialog-information";
}
}m_Columns;
static void refresh_channels(void* user_data){
auto channel_widget = (ChannelWidget *)user_data;
signal_idle().connect([channel_widget](){ //important to use an idle otherwise Thread problems
channel_widget->refresh_channel_list();
return false;
});
}
ChannelMenu::ChannelMenu(TreeView &tree_view){
MenuItem *item = new MenuItem("_Remove", true);
append(*item);
accelerate(*this);
show_all();
item->signal_activate().connect([&tree_view](){
tree_view.get_selection()->selected_foreach_iter([](const TreeModel::iterator& iter){
TreeModel::Row row = *iter;
string name= row[m_Columns.name];
blc_remove_channel_with_name(name.data());
});;
});
}
void ChannelWidget::add_process( TreeModel::Row &row){
TreeModel::Row childrow;
string command;
Process *process = new Process(blaar_dir+"/bin/", "o_gnuplot");
process->add_arg(((string)row[m_Columns.name]).data());
process->run();
command=blc_program_name;
for (auto arg = process->arg_vector.begin()+1; arg!=process->arg_vector.end()-1; arg++){
command+=" ";
command+=*arg;
}
childrow = *(tree_store->append(row.children()));
childrow[m_Columns.name]=command;
childrow[m_Columns.icon]="utilities-terminal";
childrow[m_Columns.process]= process;
show_all_children();
}
void ChannelWidget::display_process( TreeModel::Row &row){
Process *process=row[m_Columns.process];
process->display_terminal();
}
ChannelWidget::ChannelWidget(){
CellRendererPixbuf cellRendererPixBuf;
this->set_enable_search();
// this->m
blaar_dir=getenv("BLAAR_DIR");
if (blaar_dir.empty()) EXIT_ON_ERROR("Your environment variable 'BLAAR_DIR' is not defined.");
tree_store = TreeStore::create(m_Columns);
set_model(tree_store);
append_column("Name", m_Columns.name);
append_column("Type", m_Columns.type);
append_column("Format", m_Columns.format);
append_column("Dimensions", m_Columns.dimensions);
int numcols = append_column("image ", cellRendererPixBuf);
get_column(numcols-1)->add_attribute(cellRendererPixBuf, "icon-name", m_Columns.icon);
refresh_channel_list();
blc_channel_check_for_event(refresh_channels, this);
get_selection()->set_mode(SELECTION_MULTIPLE);
show_all_children();
// set_events(Gdk::BUTTON_PRESS_MASK);
signal_row_activated().connect([this](const TreeModel::Path& path, TreeViewColumn* column){
TreeIter iter=this->tree_store->get_iter(path);
TreeModel::Row row = *iter;
if (tree_store->iter_depth(iter) == 0) this->add_process(row);
else display_process(row);
});
get_selection()->signal_changed().connect([](){
return true;
});
signal_button_release_event().connect([this](GdkEventButton const *event){
if (event->button == 3){
ChannelMenu *m_Menu_Popup = new ChannelMenu(*this);
m_Menu_Popup->popup_at_pointer((GdkEvent*)event);
}
return true;
});
}
/*
void ChannelWidget::menu_add_program_item(Menu &menu, string name, string program){
MenuItem *item = new MenuItem(name);
item->signal_activate().connect([this](){
auto refSelection = get_selection();
if (refSelection) {
TreeModel::iterator iter = refSelection->get_selected();
TreeModel::Row row = *iter;
for (TreeModel::iterator iter = refSelection-> (); ){
string name= row[m_Columns.name];
blc_remove_channel_with_name(name.data());
}
}
});
menu.append(*item);
};*/
void ChannelWidget::refresh_channel_list(){
bool found;
int channels_nb;
blc_channel *channel_infos=nullptr;
channels_nb = blc_channel_get_all_infos(&channel_infos);
list<TreeModel::iterator> list_iters_without_channels;
/* Generate a list of iter on row to be able to remove the row if the channel does not exist anymore*/
tree_store->foreach_iter([&list_iters_without_channels, this](TreeModel::iterator const &iter){
if (this->tree_store->iter_depth(iter)==0) list_iters_without_channels.push_back(iter); //copy iter
return false;
});
for(blc_channel *channel=channel_infos; channel != channel_infos+channels_nb; channel++){
found=false;
auto list_iter=list_iters_without_channels.begin();
while(list_iter != list_iters_without_channels.end()){
TreeModel::Row row = *(*list_iter);
if (row[m_Columns.id]==channel->id){
found=true;
list_iter=list_iters_without_channels.erase(list_iter);
}
else list_iter++;
}
if (found==false) m_Columns.add_channel(tree_store, channel);
}
for(auto list_iter: list_iters_without_channels){
tree_store->erase(*list_iter);
}
this->show_all_children();
free(channel_infos);
}
//
// ChannelColumns.cpp
// gtk_channels
//
// Created by Arnaud Blanchard on 13/10/2019.
//
#include "ChannelColumns.hpp"
using namespace Glib;
using namespace Gtk;
using namespace std;
ChannelColumns::ChannelColumns(){
add(id);
add(name);
add(type);
add(format);
add(dimensions);
add(terminal_icon);
add(play_pause_icon);
add(play_pause_sensitive);
add(stop_icon);
add(stop_sensitive);
add(pid);
add(process);
}
void ChannelColumns::add_channel(RefPtr<TreeStore> tree, blc_channel const &channel){
uint32_t type_str, format_str;
char dims_str[256];
TreeModel::Row childrow;
auto row = *(tree->append()); //* is because it is an iterator and we want the content of the ref.
row[name] = channel.name;
row[type] = string(UINT32_TO_STRING(type_str, channel.type), 4);
row[format] = string(UINT32_TO_STRING(format_str, channel.format), 4);
channel.sprint_dims(dims_str, 256);
row[dimensions] = dims_str;
row[process]=nullptr;
row[id]=channel.id;
}
//
// ChannelColumns.hpp
// gtk_channels
//
// Created by Arnaud Blanchard on 13/10/2019.
//
#ifndef ChannelColumns_hpp
#define ChannelColumns_hpp
#include "blc_channel.h"
#include "Process.h"
#include <stdio.h>
#include <string>
#include <gtkmm.h>
struct ChannelWidget;
struct ChannelColumns : public Gtk::TreeModel::ColumnRecord{
Gtk::TreeModelColumn<int> id, pid;
Gtk::TreeModelColumn<bool> play_pause_sensitive, stop_sensitive;
Gtk::TreeModelColumn<std::string> name, type, format, dimensions, terminal_icon, play_pause_icon, stop_icon;
Gtk::TreeModelColumn<Process*> process;
ChannelWidget *channel_widget;
ChannelColumns();
void add_channel(Glib::RefPtr<Gtk::TreeStore> tree, blc_channel const &channel);
};
#endif /* ChannelColumns_hpp */
//
// Channel.cpp
// e_bash
//
// Created by Arnaud Blanchard on 29/09/2019.
//
#include "ChannelColumns.hpp"
#include "Process.h"
#include "ChannelWidget.hpp"
#include "blc_channel.h"
#include "blc_image.h"
#include <string>
#include <vector>
#include <forward_list>
#include <vte/vte.h>
#include <thread>
#include <iostream>
#include <numeric>
using namespace Gtk;
using namespace Glib; //signal_idle,RefPtr
using namespace std;
static void refresh_channels(void* user_data){
auto channel_widget = (ChannelWidget *)user_data;
signal_idle().connect([channel_widget](){ //important to use an idle otherwise Thread problems
channel_widget->refresh_channel_list();
return false;
});
}
ChannelColumns ChannelWidget::columns;
ChannelWidget::ChannelWidget(){
char const *blaar_dir=getenv("BLAAR_DIR");
if (blaar_dir==nullptr) EXIT_ON_ERROR("Your environment variable 'BLAAR_DIR' is not defined.");
program_dir=blaar_dir;
program_dir+="/bin";
append_column("Name", columns.name);
append_column("Type", columns.type);
append_column("Format", columns.format);
append_column("Dimensions", columns.dimensions);
tree_store = TreeStore::create(columns);
set_model(tree_store);
set_activate_on_single_click();
TreeViewColumn *col = new TreeViewColumn(" ", terminal_cell_renderer);
col->add_attribute(terminal_cell_renderer, "icon-name", columns.terminal_icon);
append_column(*col);
col = new TreeViewColumn(" ");
col->pack_start(play_pause_cell_renderer);
col->pack_start(stop_cell_renderer);
col->add_attribute(play_pause_cell_renderer, "icon-name", columns.play_pause_icon);
col->add_attribute(stop_cell_renderer, "icon-name", columns.stop_icon);
col->add_attribute(play_pause_cell_renderer, "sensitive", columns.play_pause_sensitive);
col->add_attribute(stop_cell_renderer, "sensitive", columns.stop_sensitive);
append_column(*col);
signal_row_activated().connect([this](const TreeModel::Path& path, TreeViewColumn* column){
int x, y;
TreeIter iter=this->tree_store->get_iter(path);
TreeModel::Row row = *iter;
Process *process=row[columns.process];
if (tree_store->iter_depth(iter) != 0){ //It is a process
if (column->get_cell_position(terminal_cell_renderer, x, y)){//x, y are not used but it allow to chack the existanace of the cell renderer
process->display_terminal();
}
else if (column->get_cell_position(play_pause_cell_renderer, x, y)){
process->signal(SIGTSTP);
}
}
});
signal_button_release_event().connect([this](GdkEventButton const *event){
if (event->button == 3){
ContextMenu *menu=create_menu();
menu->popup_at_pointer((GdkEvent*)event);
}
return true;
});
refresh_channel_list();
blc_channel_check_for_event(refresh_channels, this);
get_selection()->set_mode(SELECTION_MULTIPLE);
show_all_children();
}
blc_channel *ChannelWidget::find_channel(int id){
blc_channel *channel;
channel= lower_bound(channels, channels+channels_nb, id, [](blc_channel const &a, int id){
return (a.id < id);
});
return channel;
}
void ChannelWidget::after_launch(Process *process, std::vector<TreeModel::Path> rows){
auto process_rows=make_shared<vector<TreeIter>>();
string command = accumulate(process->arg_vector.begin()+1, process->arg_vector.end()-1, process->program_name, [](auto const &a, auto const &b){
return a+" "+b;
});
for (auto const &path : rows){
TreeNodeChildren node = tree_store->get_iter(path)->children();
TreeIter row_iter=tree_store->append(node);
process_rows->emplace_back(row_iter);
TreeRow row=*row_iter;
row[columns.name]=command;
row[columns.terminal_icon]="utilities-terminal";
row[columns.play_pause_icon]="media-playback-pause";
row[columns.play_pause_sensitive]=true;
row[columns.stop_icon]="media-playback-stop";
row[columns.stop_sensitive]=true;
row[columns.process]= process;
expand_row(path, true);
// process_rows->push_back(row_iter);