From 539bc0b81417995d2a40875365e812b3e6bad32b Mon Sep 17 00:00:00 2001
From: Arnaud Blanchard <arnaud.blanchard@ensea.fr>
Date: Thu, 10 Dec 2020 19:47:15 +0100
Subject: [PATCH] Add network client, an dC++ style

---
 c_gtk_image/CMakeLists.txt     |  19 +-
 c_gtk_image/Histogram.hpp      |  16 +
 c_gtk_image/c_gtk_image.cpp    | 126 +++----
 gtk_channels/ChannelWidget.cpp | 218 ++++++-----
 gtk_channels/ChannelWidget.hpp |  11 +-
 gtk_channels/Command.hpp       |   3 +
 gtk_channels/Process.cpp       |   1 -
 include/Display.hpp            |  50 +++
 include/Histogram.hpp          |  40 +++
 include/Image.hpp              |  49 +++
 include/UIN8_Y800.hpp          |  35 ++
 include/common.h               |  34 --
 o_gtk_image/CMakeLists.txt     |  12 +-
 src/Display.cpp                | 190 ++++++++++
 src/Image.cpp                  |  92 +++++
 src/UIN8_UYVY.cpp              |  82 +++++
 src/UIN8_Y800.cpp              |  71 ++++
 src/histogram.cpp              | 234 ++----------
 src/image_display.cpp          | 637 ---------------------------------
 19 files changed, 856 insertions(+), 1064 deletions(-)
 create mode 100644 c_gtk_image/Histogram.hpp
 create mode 100644 include/Display.hpp
 create mode 100644 include/Histogram.hpp
 create mode 100644 include/Image.hpp
 create mode 100644 include/UIN8_Y800.hpp
 delete mode 100644 include/common.h
 create mode 100644 src/Display.cpp
 create mode 100644 src/Image.cpp
 create mode 100644 src/UIN8_UYVY.cpp
 create mode 100644 src/UIN8_Y800.cpp
 delete mode 100644 src/image_display.cpp

diff --git a/c_gtk_image/CMakeLists.txt b/c_gtk_image/CMakeLists.txt
index 588efeb..d6a57c6 100644
--- a/c_gtk_image/CMakeLists.txt
+++ b/c_gtk_image/CMakeLists.txt
@@ -1,26 +1,23 @@
 # Set the minimum version of cmake required to build this project
-cmake_minimum_required(VERSION 2.6)
+cmake_minimum_required(VERSION 3.13)
 
 project(c_gtk_image)
 
 #This is to be able to debug and recompile automatically the libs (shared_blc). It is slower, you can remove this line and use  :  ${BLAR_BUILD_DIR}/lib/libblc.dylib instead of shared_blc
 
-find_package(blc_network REQUIRED)
-find_package(blc_channel REQUIRED) #For compatibilité with common.h
-
-find_package(blc_image REQUIRED)  
-find_package(blc_program REQUIRED)
+find_package(blc REQUIRED)
 
 find_package(PkgConfig REQUIRED) 
 pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
 find_package(JPEG REQUIRED) 
 
-add_definitions(-Wall ${BL_DEFINITIONS} -std=c++17 -Wno-deprecated-declarations) #device_manager (mouse) is deprecated
-include_directories(${GTK3_INCLUDE_DIRS} ${BL_INCLUDE_DIRS} ${JPEG_INCLUDE_DIR} ../include)
-link_directories(${GTK3_LIBRARY_DIRS})
+add_definitions(-Wall  -Wno-deprecated-declarations) #device_manager (mouse) is deprecated
 add_definitions(${GTK3_CFLAGS_OTHER})
-add_executable(c_gtk_image c_gtk_image.cpp ../src/image_display.cpp ../src/histogram.cpp)
-target_link_libraries(c_gtk_image ${GTK3_LIBRARIES} ${BL_LIBRARIES} ${JPEG_LIBRARIES} )
+add_executable(c_gtk_image c_gtk_image.cpp ../src/Display.cpp ../src/Image.cpp ../src/Histogram.cpp  ../src/UIN8_Y800.cpp  )
+target_compile_features(c_gtk_image PRIVATE cxx_std_17)
+target_include_directories(c_gtk_image PRIVATE ${GTK3_INCLUDE_DIRS} ${JPEG_INCLUDE_DIR} ../include)
+target_link_directories(c_gtk_image PRIVATE ${GTK3_LIBRARY_DIRS})
+target_link_libraries(c_gtk_image PRIVATE blc ${GTK3_LIBRARIES} ${JPEG_LIBRARIES} )
 
 
 
diff --git a/c_gtk_image/Histogram.hpp b/c_gtk_image/Histogram.hpp
new file mode 100644
index 0000000..2ce45bb
--- /dev/null
+++ b/c_gtk_image/Histogram.hpp
@@ -0,0 +1,16 @@
+//
+//  Histogram.hpp
+//  c_gtk_image
+//
+//  Created by Arnaud Blanchard on 23/11/2020.
+//
+
+#ifndef Histogram_hpp
+#define Histogram_hpp
+
+class Histogram:public blc_array{
+public:
+    Histogram(blc_array const &array);
+}
+
+#endif /* Histogram_h */
diff --git a/c_gtk_image/c_gtk_image.cpp b/c_gtk_image/c_gtk_image.cpp
index e5d4a48..cb27ccc 100644
--- a/c_gtk_image/c_gtk_image.cpp
+++ b/c_gtk_image/c_gtk_image.cpp
@@ -1,5 +1,3 @@
-#include "common.h"
-
 #include <fcntl.h> // O_RDONLY ...
 #include <stdio.h>
 #include <gtk/gtk.h>
@@ -8,25 +6,50 @@
 //#include <jpeglib.h>
 #include <sys/mman.h>
 #include <errno.h> //errno
-#include "blc_core.h"
-#include "blc_program.h"
-#include "blc_network.h"
 #include <thread>
+#include <string>
+#include "blc.h"
+#include "Display.hpp"
 
 using namespace std;
 
-float min_val, max_val;
+class Client:public blc_array_tcp4_client{
+    GtkWidget *window;
+public:
+    Client(string const &address, string const &port);
+    void network_manager();
+    unique_ptr<Display> display;
+    unique_ptr<thread>  network_thread;
+};
+
+gboolean update_data(GtkWidget *widget, GdkFrameClock *frame_clock, void *user_data){
+    auto client=reinterpret_cast<Client*>(user_data);
+    client->recv_data();
+    client->display->update();
+    return G_SOURCE_CONTINUE;
+}
+
+Client::Client(string const &address, string const &port_name):blc_array_tcp4_client(address, port_name){
+    string title(string(address)+":"+string(port_name));
+    
+    window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title(GTK_WINDOW(window), title.c_str());
+    
+    display=make_unique<Display>(*this);
+    gtk_container_add(GTK_CONTAINER(window), display->widget);
+    gtk_widget_show_all(window);
+    gtk_widget_add_tick_callback (display->widget, update_data, this, nullptr);
 
+}
 
 const char *channel_name, *fullscreen_option, *keyboard_mode, *address, *port_name;
 GtkWidget *window;
 GdkDisplay *main_display;
-GdkDevice *pointer_device;
 int interactive_mode=0;
 GtkApplication *app;
 
 blc_array mouse_array;
-blc_array input;
+unique_ptr<Client> client;
 
 char const *input_name;
 
@@ -38,19 +61,15 @@ uint32_t format;
 
 void ask_fullscreen(){
     gtk_window_fullscreen(GTK_WINDOW(window));
-    
 }
 
-void ask_quit()
-{
+void ask_quit(){
     g_application_quit(G_APPLICATION(app));
     blc_quit();
 }
 
-void on_key_press(GtkWidget	     *widget, GdkEventKey	     *event){
-    
+void on_key_press(GtkWidget	*widget, GdkEventKey *event){
     char key;
-    
     if (event->type == GDK_KEY_PRESS){
         key=event->keyval;
         fwrite(&key, 1, 1, stdout);
@@ -58,42 +77,23 @@ void on_key_press(GtkWidget	     *widget, GdkEventKey	     *event){
     }
 }
 
-void activate_cb(GApplication *app)
-{
-    GtkWidget *display=nullptr;
-    GtkWidget *grid;
-    string title(string(address)+":"+string(port_name));
-    
-    main_display = gdk_display_get_default ();
-    GdkDeviceManager *device_manager = gdk_display_get_device_manager (main_display);
-    pointer_device = gdk_device_manager_get_client_pointer (device_manager);
-    
+
+void activate_cb(GApplication *app, gpointer){
     window=gtk_application_window_new(GTK_APPLICATION(app));
-    gtk_window_set_title(GTK_WINDOW(window), title.c_str());
-    
-    grid=gtk_grid_new();
-    
-    //  for(i=0; input_names[i]; i++){ This is for displaying multiple images
-    //  input=new blc_channel(/*input_names[i]*/ input_name, BLC_CHANNEL_READ);
-    display=create_image_display(&input);
-    if (display==NULL) EXIT_ON_ARRAY_ERROR(&input, "Format not managed.");
-    gtk_widget_set_hexpand(display, 1);
-    gtk_widget_set_vexpand(display, 1);
-    gtk_container_add(GTK_CONTAINER(grid), display);
-    //  }
-    gtk_container_add(GTK_CONTAINER(window), grid);
+    gtk_window_set_title( GTK_WINDOW(window), "c_gtk_image");
+    client=make_unique<Client>(address, port_name);
+    if (client->display==nullptr) EXIT_ON_ARRAY_ERROR(client.get(), "Format not managed.");
+    if (keyboard_mode) g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK (on_key_press), nullptr);
     gtk_widget_show_all(window);
-    if (keyboard_mode) g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK (on_key_press), NULL);
-    
+
 }
 
 /** Classical GTK application.
  * The first optional argument is the name of the experience. Otherwise all the existing shared memory are used.
  * */
-int main(int argc, char *argv[])
-{
+int main(int argc, char *argv[]){
     int status=0;
-    char const *g_debug, *mouse_name, *min_str, *max_str;
+    char const *g_debug, *min_str, *max_str;
     
     blc_program_set_description("Display the content of the blc_channel depending on its type on format");
     blc_program_add_option(&keyboard_mode, 'k', "keyboard", nullptr, "Send keyboard input to stdout", nullptr);
@@ -101,45 +101,23 @@ int main(int argc, char *argv[])
     blc_program_add_option(&max_str, 'M', "max", "FL32", "maximal value", nullptr);
     blc_program_add_option(&fullscreen_option, 'F', "fullscreen", nullptr, "Set the window in fullscreen", nullptr);
     blc_program_add_option(&g_debug, 'g', "g-fatal-warnings", nullptr, "Debug gtk.", nullptr);
-    blc_program_add_parameter(&address, "address", 1, "internet address", "localhost");
-    blc_program_add_parameter(&port_name, "port", 1, "port name", "31440");
-    
+    blc_program_add_parameter(&address, "address", 0, "internet address", "localhost");
+    blc_program_add_parameter(&port_name, "port", 0, "port name", "31440");
     blc_program_init(&argc, &argv, ask_quit);
-    blc_command_forward_blc_channels();
-
-    blc_array_tcp4_client input_client(&input, address, port_name);
-    input.allocate();
-    fprintf(stderr, "Connecté\n");
-        
-    min_val=0;
-    if (input.type=='UIN8') max_val=256;
-    if (input.type=='INT8'){
-        min_val=-128;
-        max_val=128;
-    }
-    
-    if (input.type=='FL32' && input.format=='Y800'){
-        if (min_str) SSCANF(1, min_str, "%f", &min_val);
-        else min_val=0;
-        if (max_str) SSCANF(1, max_str, "%f", &max_val);
-        else max_val=1;
-    }else if (min_str || max_str) EXIT_ON_ARRAY_ERROR(&input, "Min (-m) and max (-M) have only effect for type FL32 format Y800");
-    
-
-    thread input_thread([&input_client](){
-        while(1) {
-            input_client.read_data();
-        }
-    });
 
+    if (g_debug) {
+        argc=2;
+        argv=MANY_ALLOCATIONS(3, char*);
+        argv[0]=strdup("Program");
+        argv[1]=strdup("--g-fatal-warnings");
+        argv[2]=nullptr;
+    }
     gtk_disable_setlocale();
     gtk_init(&argc, &argv);
     app = gtk_application_new(nullptr, G_APPLICATION_FLAGS_NONE);
     g_signal_connect (app, "activate", G_CALLBACK (activate_cb), nullptr);
     status = g_application_run(G_APPLICATION(app), 0, nullptr);
     g_object_unref(app);
-    
-    input_thread.join();
-    return EXIT_SUCCESS;
+    return status;
 }
 
diff --git a/gtk_channels/ChannelWidget.cpp b/gtk_channels/ChannelWidget.cpp
index 9d562a1..9cabb33 100644
--- a/gtk_channels/ChannelWidget.cpp
+++ b/gtk_channels/ChannelWidget.cpp
@@ -1,7 +1,4 @@
 //
-//  Channel.cpp
-//  e_bash
-//
 //  Created by Arnaud Blanchard on 29/09/2019.
 //
 
@@ -58,24 +55,25 @@ ChannelWidget::ChannelWidget(){
     col->add_attribute(stop_cell_renderer, "sensitive", columns.stop_sensitive);
     append_column(*col);
     
-    
-
-    commands.emplace("graph", Command(bin_dir, "o_gnuplot", {}, [](vector<blc_channel const*> channels ){
+    default_commands.emplace("graph", Command(bin_dir, "o_gnuplot", {}, [](vector<blc_channel const*> channels ){
         if (channels.size()>1) return false;
-      }));
-    commands.emplace("graph history", Command(bin_dir, "o_gnuplot", {"--history=100"}, [](vector<blc_channel const*> channels ){
-         int total_length=channels[0]->total_length;
-         return all_of(channels.begin()+1,channels.end(),[total_length](blc_channel const *channel){
-             if (channel->dims_nb==1 && channel->total_length!=total_length) return false;
-             else if (channel->dims_nb==2) return false;
-             return true;
-         });
-       }));
-    commands.emplace("keyboard", Command(bin_dir, "i_keyboard", {"--debug"}, [](vector<blc_channel const*> channels ){
+        return true;
+    }));
+    
+    default_commands.emplace("graph history", Command(bin_dir, "o_gnuplot", {"--history=100"}, [](vector<blc_channel const*> channels ){
+        int total_length=channels[0]->total_length;
+        return all_of(channels.begin()+1,channels.end(),[total_length](blc_channel const *channel){
+            if (channel->dims_nb==1 && channel->total_length!=total_length) return false;
+            else if (channel->dims_nb==2) return false;
+            return true;
+        });
+    }));
+    
+    default_commands.emplace("keyboard", Command(bin_dir, "i_keyboard", {"--debug"}, [](vector<blc_channel const*> channels ){
         if (channels.size()==1 && channels[0]->total_length<=64) return true;
         else return false;
     }));
-
+    
     
     signal_row_activated().connect([this](const TreeModel::Path& path, TreeViewColumn* column){
         int x, y;
@@ -110,7 +108,7 @@ ChannelWidget::ChannelWidget(){
     
     signal_button_release_event().connect([this](GdkEventButton const *event){
         if  (event->button == 3){
-            ContextMenu *menu=create_menu();
+            ContextMenu *menu=create_popup_menu();
             menu->popup_at_pointer((GdkEvent*)event);
         }
         return true;
@@ -122,8 +120,9 @@ ChannelWidget::ChannelWidget(){
     show_all_children();
 }
 
+
 blc_channel *ChannelWidget::find_channel(int  id){
-    blc_channel *channel= lower_bound(channels, channels+channels_nb, id, [](blc_channel const &a, int id){
+    blc_channel *channel = &lower_bound(channels.begin(), channels.end(), id, [](blc_channel const &a, int id){
         return (a.id < id);
     });
     return channel;
@@ -174,41 +173,41 @@ void ChannelWidget::on_quit(Process *process, int status, std::vector<TreeModel:
     }
 }
 /*
-void ChannelWidget::create_and_execute_command(string program_name, initializer_list<const char*> arguments){
-    commands.emplace_back(bin_dir, program_name, arguments));
-    auto command=&commands.back();
-    command->execute();
-}*/
+ void ChannelWidget::create_and_execute_command(string program_name, initializer_list<const char*> arguments){
+ commands.emplace_back(bin_dir, program_name, arguments));
+ auto command=&commands.back();
+ command->execute();
+ }*/
 
 /*
-void ContextMenu::add_program(char const *menu_name, std::string program_name, initializer_list<const char*> const optional_arguments, function <void(Process *process)> after_launch){
-    auto item=new MenuItem(menu_name);
-    auto args_vec=make_shared<vector<const char*>>(optional_arguments); //This will be destroyed by process
-    append(*item);
-    
-    item->signal_activate().connect([this, args_vec, program_name, after_launch]{
-        args_vec->reserve(args_vec->size()+selected_channels.size());
-        for(auto const &channel:selected_channels){
-            args_vec->push_back(strdup(channel->name));
-        }
-        auto process = new Process(program_dir, program_name, args_vec);
-        process->run(after_launch);
-    });
-}*/
+ void ContextMenu::add_program(char const *menu_name, std::string program_name, initializer_list<const char*> const optional_arguments, function <void(Process *process)> after_launch){
+ auto item=new MenuItem(menu_name);
+ auto args_vec=make_shared<vector<const char*>>(optional_arguments); //This will be destroyed by process
+ append(*item);
+ 
+ item->signal_activate().connect([this, args_vec, program_name, after_launch]{
+ args_vec->reserve(args_vec->size()+selected_channels.size());
+ for(auto const &channel:selected_channels){
+ args_vec->push_back(strdup(channel->name));
+ }
+ auto process = new Process(program_dir, program_name, args_vec);
+ process->run(after_launch);
+ });
+ }*/
 
 
 /*
-item->signal_activate().connect([this, args_vec, program_name, after_launch]{
-    args_vec->reserve(args_vec->size()+selected_channels.size());
-    for(auto const &channel:selected_channels){
-        args_vec->push_back(strdup(channel->name));
-    }
-    auto process = new Process(program_dir, program_name, args_vec);
-    process->run(after_launch);
-});
+ item->signal_activate().connect([this, args_vec, program_name, after_launch]{
+ args_vec->reserve(args_vec->size()+selected_channels.size());
+ for(auto const &channel:selected_channels){
+ args_vec->push_back(strdup(channel->name));
+ }
+ auto process = new Process(program_dir, program_name, args_vec);
+ process->run(after_launch);
+ });
  */
 
-ContextMenu *ChannelWidget::create_menu(){
+ContextMenu *ChannelWidget::create_popup_menu(){
     bool only_channels=true;
     vector<Command*> context_commands;
     vector<blc_channel const *> selected_channels;
@@ -216,12 +215,12 @@ ContextMenu *ChannelWidget::create_menu(){
     menu->row_selection=get_selection();
     
     get_selection()->selected_foreach_iter ([&](TreeModel::iterator const &iter){
-           if (tree_store->iter_depth(iter)==0){
-               blc_channel *channel=find_channel((*iter)[columns.id]);
-               selected_channels.emplace_back(channel);
-           }
-           else only_channels=false;
-       });
+        if (tree_store->iter_depth(iter)==0){
+            blc_channel *channel=find_channel((*iter)[columns.id]);
+            selected_channels.emplace_back(channel);
+        }
+        else only_channels=false;
+    });
     
     if (only_channels){
         menu->add_item("_Remove", true, [this]{
@@ -231,12 +230,46 @@ ContextMenu *ChannelWidget::create_menu(){
         });
         menu->add_separator();
         
-        for ( const auto&  [name, command]:commands){
+        for ( const auto&  [name, command]:default_commands){
             Command const *command_ptr=&command; //wihout ptr, command cannot be pass to lambda
             if (command.valid_channels(selected_channels));
             menu->add_item(name, false, [this, command_ptr, selected_channels](){
                 auto command =used_commands.emplace_back(*command_ptr, selected_channels);
-                auto process=new Process(command.args.data(), [](Process*){});
+                Glib::RefPtr<TreeModel> tree_model = tree_store;
+                auto paths=this->get_selection()->get_selected_rows(tree_model);
+                vector<TreeRow> command_rows;
+                
+                for (auto const &path: paths){
+                    string full_command = accumulate(command.args.begin()+1, command.args.end()-1, command.program_name,[](string const a, string const b){
+                        return a + " " +b;
+                    });
+                    
+                    auto iter=tree_store->get_iter(path);
+                    if (tree_store->iter_depth(iter)==0) {
+                        TreeRow &row=command_rows.emplace_back(*tree_store->append((iter)->children()));
+                        row[columns.name]=full_command;
+                        row[columns.terminal_icon]="utilities-terminal";
+                        expand_row(path, true);
+                    }
+                }
+                
+                auto process=new Process(command.args.data(), [command_rows](Process* process){
+                    for (auto const &row:command_rows){
+                        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;
+                    }
+                    process->on_quit([command_rows](int status){
+                        for (auto const &row :command_rows){
+                            if (status==0)  row[columns.terminal_icon]="process-stop";
+                            else row[columns.terminal_icon]="dialog-error";
+                            row[columns.play_pause_icon]="media-playback-start";
+                            row[columns.stop_sensitive]=false;
+                        }
+                    });
+                });
             });
         }
     }
@@ -244,38 +277,53 @@ ContextMenu *ChannelWidget::create_menu(){
     return menu;
 }
 
+template <class _Container>
+insert_iterator<_Container> remove_channel(_Container& __x, typename _Container::iterator __i)
+{
+    return insert_iterator<_Container>(__x, __i);
+}
+
 /**
  We look for all channels. We get a list of actual row and look for differences to add or remove channel in the known list.
  This suppose that a channel with a same id has the same properties. It is a property of blc_channel.
  */
 void ChannelWidget::refresh_channel_list(){
-    ///  We define a functor to compare blc_channel and TreeIter by id
-    struct compare_id{
-        bool operator()(blc_channel const &a, TreeIter const &b) { return a.id < (*b)[columns.id]; };
-            bool operator()(TreeIter const &a, blc_channel const &b) { return  (*a)[columns.id]< b.id ; };
-            };
-            vector<blc_channel> news_channels;
-            vector<TreeIter> channels_to_remove;
-            
-            FREE(channels);
-            tie(channels, channels_nb)=blc_channel::get_all_infos();
-            
-            //We get children to be able to use iterator
-            auto children=tree_store->children();
-            
-            //We get a list of new channels
-            set_difference(channels, channels+channels_nb, children.begin(), children.end(), inserter(news_channels, news_channels.begin()), compare_id());
-            
-            //We get a list of channels to remove
-            set_difference(children.begin(), children.end(), channels, channels+channels_nb, inserter(channels_to_remove, channels_to_remove.begin()), compare_id());
-            
-            for(auto const &tree_iter : channels_to_remove)  tree_store->erase(tree_iter);
-            for(auto const &channel :news_channels)  columns.add_channel(tree_store, channel);
-            
-            this->show_all_children();
-            
-            //We free the list of channel before getting a new list. This is not optimum but it does not happen often.
-            for_each(channels, channels+channels_nb, [](blc_channel channel){
-                channel.~blc_channel(); //We destroy the dims inside
-            });
-            }
+    auto [local_channels, local_channels_nb] = blc_channel::get_all_infos();
+    
+    //We get children to be able to use iterator
+    auto children=tree_store->children();
+    
+    for(auto const &child : children){
+        int id = child[columns.id];
+        auto it=lower_bound(local_channels,local_channels+local_channels_nb, id);
+        if (it==local_channels+local_channels_nb){
+            tree_store->erase(child);
+            channels.erase(id);
+        }
+    }
+    
+    for_each(local_channels, local_channels+local_channels_nb, [](blc_channel const &channel){
+  //      find_if([](){
+  //          columns.add_channel(tree_store, channel);
+        
+        });
+    });
+
+    
+    
+    for(auto const &tree_iter : channels_to_remove)  {
+        tree_store->erase(tree_iter);
+    }
+    
+    for(auto const &channel :news_channels) {
+    }
+    
+    this->show_all_children();
+    
+    //We free the list of channel before getting a new list. This is not optimum but it does not happen often.
+    for_each(local_channels, local_channels+local_channels_nb, [](blc_channel channel){
+        channel.~blc_channel(); //We destroy the dims inside
+    });
+    FREE(channels);
+    
+}
diff --git a/gtk_channels/ChannelWidget.hpp b/gtk_channels/ChannelWidget.hpp
index b6b5e88..9996c85 100644
--- a/gtk_channels/ChannelWidget.hpp
+++ b/gtk_channels/ChannelWidget.hpp
@@ -21,22 +21,23 @@
 
 
 struct ChannelWidget:Gtk::TreeView{
-    blc_channel* channels;  //This structure is coherent with blc_channel implementation
+    std::map<int, blc_channel> channels;
     ContextMenu *menu=nullptr;
     Gtk::CellRendererPixbuf  terminal_cell_renderer, play_pause_cell_renderer, stop_cell_renderer;
     Glib::RefPtr<Gtk::TreeStore>  tree_store;
-    int channels_nb;
     std::string bin_dir;
-    std::map<std::string, Command> commands;
     std::vector<Command> used_commands;
+    
+    std::vector<Gtk::TreeRow> channel_rows;
+    
+    static std::map<std::string, Command> default_commands;
     static ChannelColumns columns;
 
     ChannelWidget();
     blc_channel *find_channel(int  id);
-    ContextMenu *create_menu();
+    ContextMenu *create_popup_menu();
     void create_and_execute_command(std::string program_name, std::initializer_list<const char*> const optional_arguments);
 
-
     void display_process(Gtk::TreeModel::Row &row);
     void refresh_channel_list();
     void after_launch(Process *process, std::vector<Gtk::TreeModel::Path> rows);
diff --git a/gtk_channels/Command.hpp b/gtk_channels/Command.hpp
index ac6f02f..ebd3df6 100644
--- a/gtk_channels/Command.hpp
+++ b/gtk_channels/Command.hpp
@@ -10,15 +10,18 @@
 
 #include <string>
 #include <vector>
+#include <gtkmm.h>
 #include "Process.h"
 #include "blc_channel.h"
 
+
 /* One command may have many processes */
 struct Command{
     std::string binary;
     std::string program_name;
     std::function<bool(std::vector<blc_channel const*> &channels)> valid_channels;
     std::vector<const char *> args;
+    std::vector<Gtk::TreeRow> command_rows;
     
     Command(std::string const &bin_dir, std::string program_name, std::initializer_list<char const*> default_args, std::function<bool(std::vector<blc_channel const*>)>validity);
     Command(Command const &command, std::vector<blc_channel const*> const channel); //Copy command and add arguments
diff --git a/gtk_channels/Process.cpp b/gtk_channels/Process.cpp
index 41832ed..8f0430a 100644
--- a/gtk_channels/Process.cpp
+++ b/gtk_channels/Process.cpp
@@ -53,7 +53,6 @@ Process::Process(char const * const *argv, function<void(Process *)>after_launch
     
 };
 
-
 void Process::run(){
     vte_terminal_spawn_async(terminal, VTE_PTY_DEFAULT , nullptr, (char**)arg_vector.data(), nullptr, G_SPAWN_DEFAULT, nullptr, nullptr, nullptr, 1000, nullptr, TerminalSpawnAsyncCallback, (void*)this);
 }
diff --git a/include/Display.hpp b/include/Display.hpp
new file mode 100644
index 0000000..e6930e6
--- /dev/null
+++ b/include/Display.hpp
@@ -0,0 +1,50 @@
+//
+//  Display.hpp
+//  c_gtk_image
+//
+//  Created by Arnaud Blanchard on 12/11/2020.
+//
+
+#ifndef Display_hpp
+#define Display_hpp
+
+#include "UIN8_Y800.hpp"
+#include "blc_array.h"
+#include "cairo.h"
+#include "gtk/gtk.h"
+
+class Display:public blc_array{
+public:
+    Display(blc_array const &array);
+    ~Display();
+    void update();
+    void framerate();
+    void update_zoom();
+        
+    static void histogram_cb();
+
+    double min_val, max_val;
+    int iterations_nb;
+    
+    GtkWidget *histogram_button;
+    GtkWidget *widget;
+    GtkWidget *fit_button;
+    std::unique_ptr<Image> image;
+    std::unique_ptr<Histogram> histogram;
+    GtkWidget *toolbar, *general_statusbar, *framerate_statusbar, *pointer_statusbar, *panel;
+};
+
+
+class UIN8_UYVY_display:public Display{
+    void snprint_pixel(char *text, int size_max, int i, int j);
+    void snprint_histogram(char *text, int size_max, int i);
+public:
+    UIN8_UYVY_display(blc_array const &array);
+    void update();
+    void show_histogram();
+    void hide_histogram();
+    
+    uint32_t *image_data;
+    static uint32_t *RGBA_from_YUYV;
+};
+#endif /* Display_hpp */
diff --git a/include/Histogram.hpp b/include/Histogram.hpp
new file mode 100644
index 0000000..f1fbcba
--- /dev/null
+++ b/include/Histogram.hpp
@@ -0,0 +1,40 @@
+//
+//  Histogram.hpp
+//  c_gtk_image
+//
+//  Created by Arnaud Blanchard on 12/11/2020.
+//
+
+#ifndef Histogram_h
+#define Histogram_h
+
+#include <vector>
+#include <blc_array.h>
+#include <cairo.h>
+#include <gtk/gtk.h>
+#include <string>
+
+class Histogram:public blc_array{
+    static void resize_cb(GtkWidget *widget, GdkRectangle *allocation, Histogram *histogram);
+protected:
+    void refresh();
+    double x_scale, y_scale;
+public:
+    Histogram(blc_array const &array);
+    virtual ~Histogram() = default;
+    bool get_pointer_position(int &i);
+    
+    virtual void update() = 0;
+    virtual void update_pointer_info()=0 ;
+
+    std::string pointer_info;
+    cairo_surface_t *surface;
+    size_t width, height;
+    int iterations_nb;
+    uchar *surface_data;
+    GdkWindow *window;
+    GdkDevice *pointer_device;
+    GtkWidget *toolbar, *general_statusbar, *framerate_statusbar, *pointer_statusbar;
+    GtkWidget *widget, *image, *scrolled_window, *legend_box;
+};
+#endif /* Histogram_h */
diff --git a/include/Image.hpp b/include/Image.hpp
new file mode 100644
index 0000000..dd015ba
--- /dev/null
+++ b/include/Image.hpp
@@ -0,0 +1,49 @@
+//
+//  Image.hpp
+//  c_gtk_image
+//
+//  Created by Arnaud Blanchard on 23/11/2020.
+//
+
+#ifndef Image_hpp
+#define Image_hpp
+
+#include "blc_array.h"
+#include <gtk/gtk.h>
+#include <cairo.h>
+#include <string>
+
+
+class Image:public blc_array{
+    bool fit_size=true;
+public:
+    
+    Image(blc_array const &array);
+    virtual ~Image();
+    void resize(int allocated_width, int allocated_height);
+    void zoom_original();
+    void zoom(double factor);
+    void active_fit_size();
+    
+    /** set pointer position (i, j) in the window. return false if the pointer is not in the window. */
+    bool get_pointer_position(int &i, int &j);
+    
+  
+    /** Display the new image with the content array.*/
+    virtual void update()=0;
+    
+    static void resize_cb(GtkWidget *widget,  GdkRectangle *allocation, Image *image);
+
+
+    std::string pointer_info;
+    double x_scale, y_scale;
+    std::string pixel_info();
+    cairo_surface_t *surface;
+    GdkDevice* pointer_device;
+    GdkWindow *window;
+    GtkWidget *widget, *image;
+    size_t width, height;
+};
+
+
+#endif /* Image_h */
diff --git a/include/UIN8_Y800.hpp b/include/UIN8_Y800.hpp
new file mode 100644
index 0000000..71381be
--- /dev/null
+++ b/include/UIN8_Y800.hpp
@@ -0,0 +1,35 @@
+//
+//  UIN8_Y800.hpp
+//  c_gtk_image
+//
+//  Created by Arnaud Blanchard on 23/11/2020.
+//
+
+#ifndef UIN8_Y800_hpp
+#define UIN8_Y800_hpp
+
+#include "Image.hpp"
+#include "Histogram.hpp"
+#include <vector>
+
+class UIN8_Y800_image:public Image{
+    std::vector<uint32_t> color_map;
+    uint32_t *pixels;
+
+public:
+    UIN8_Y800_image(blc_array const &array);
+    void update();
+    void update_pointer_info();
+};
+
+class UIN8_Y800_histogram:public Histogram{
+    std::vector<uint32_t>color_map;
+    std::vector<uint8_t>histogram;
+    uint32_t *pixels;
+    
+public:
+    UIN8_Y800_histogram(blc_array const &array);
+    void update();
+    void update_pointer_info();
+};
+#endif /* UIN8_Y800_h */
diff --git a/include/common.h b/include/common.h
deleted file mode 100644
index 880b852..0000000
--- a/include/common.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef COMMON_H
-#define COMMON_H
-
-#include <gtk/gtk.h>
-#include <blc_core.h>
-
-
-extern float min_val, max_val;
-
-extern blc_array *array;
-extern GtkWidget *legend;
-
-extern u_int32_t false_colors[256];
-extern uint32_t gray_colors[256];
-extern uint32_t u_colors[256];
-extern uint32_t v_colors[256];
-extern uint32_t r_colors[256];
-extern uint32_t g_colors[256];
-extern uint32_t b_colors[256];
-extern uint32_t *color_map;
-
-extern GtkWidget *window, *paned;
-extern GdkDevice *pointer_device;
-extern blc_array mouse_array;
-
-extern char const *fullscreen_option;
-
-
-
-void histogram_cb(GtkToolButton *toolbutton, gpointer pointer_statusbar );
-GtkWidget *create_image_display(blc_array *array);
-
-#endif
-
diff --git a/o_gtk_image/CMakeLists.txt b/o_gtk_image/CMakeLists.txt
index a9f91ec..89e6e96 100644
--- a/o_gtk_image/CMakeLists.txt
+++ b/o_gtk_image/CMakeLists.txt
@@ -1,24 +1,22 @@
 # Set the minimum version of cmake required to build this project
-cmake_minimum_required(VERSION 2.6)
+cmake_minimum_required(VERSION 3.13)
 
 project(o_gtk_image)
 
 #This is to be able to debug and recompile automatically the libs (shared_blc). It is slower, you can remove this line and use  :  ${BLAR_BUILD_DIR}/lib/libblc.dylib instead of shared_blc
 
-find_package(blc_channel REQUIRED)    
-find_package(blc_image REQUIRED)  
-find_package(blc_program REQUIRED)
+find_package(blc REQUIRED)
 
 find_package(PkgConfig REQUIRED) 
 pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
 find_package(JPEG REQUIRED) 
 
-add_definitions(-Wall ${BL_DEFINITIONS} -Wno-deprecated-declarations) #device_manager (mouse) is deprecated
+add_definitions(-Wall  -Wno-deprecated-declarations) #device_manager (mouse) is deprecated
 include_directories(${GTK3_INCLUDE_DIRS} ${BL_INCLUDE_DIRS} ${JPEG_INCLUDE_DIR} ../include)
 link_directories(${GTK3_LIBRARY_DIRS})
 add_definitions(${GTK3_CFLAGS_OTHER})
-add_executable(o_gtk_image o_gtk_image.cpp ../src/image_display.cpp ../src/histogram.cpp)
-target_link_libraries(o_gtk_image ${GTK3_LIBRARIES} ${BL_LIBRARIES} ${JPEG_LIBRARIES} )
+add_executable(o_gtk_image  o_gtk_image.cpp ../src/image_display.cpp ../src/histogram.cpp)
+target_link_libraries(o_gtk_image PRIVATE blc ${GTK3_LIBRARIES} ${JPEG_LIBRARIES} )
 
 
 
diff --git a/src/Display.cpp b/src/Display.cpp
new file mode 100644
index 0000000..8759272
--- /dev/null
+++ b/src/Display.cpp
@@ -0,0 +1,190 @@
+//
+//  Display.cpp
+//  c_gtk_image
+//
+//  Created by Arnaud Blanchard on 12/11/2020.
+//
+#include "Display.hpp"
+
+using namespace std;
+
+static GtkToggleToolButton *blgtk_add_toggle_tool_button(GtkWidget *toolbar, char const *label, char const *icon_name, GCallback callback, void *user_data)
+{
+    GtkToggleToolButton *toggle_tool_button;
+    
+    toggle_tool_button=GTK_TOGGLE_TOOL_BUTTON(gtk_toggle_tool_button_new());
+    gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toggle_tool_button), icon_name);
+    gtk_tool_button_set_label(GTK_TOOL_BUTTON(toggle_tool_button), label);
+    gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(toggle_tool_button));
+    if (callback) g_signal_connect(G_OBJECT(toggle_tool_button), "clicked", callback, user_data);
+    return toggle_tool_button;
+}
+
+static GtkWidget *blgtk_add_tool_button(GtkWidget *toolbar, gchar const *label, gchar const *icon_name, GCallback callback, gpointer user_data)
+{
+    GtkWidget *tool_button;
+    
+    tool_button=GTK_WIDGET(gtk_tool_button_new(gtk_image_new_from_icon_name(icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR), label));
+    gtk_container_add(GTK_CONTAINER(toolbar), tool_button);
+    if (callback) g_signal_connect(G_OBJECT(tool_button), "clicked", callback, user_data);
+    return tool_button;
+}
+
+static void toggle_fullscreen(GtkWidget *widget, GdkEventWindowState *event, Display *display){
+    
+    if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN ){
+        gtk_widget_hide(display->toolbar);
+        gtk_widget_hide(display->general_statusbar);
+    }
+    else{
+        gtk_widget_show(display->toolbar);
+        gtk_widget_show(display->general_statusbar);
+    }
+}
+
+static void zoom_original_cb(GtkToolButton *, Display *display){
+    display->image->zoom_original();
+}
+
+static void zoom_out_cb(GtkToolButton *toolbutton, Display *display){
+    display->image->zoom(2);
+}
+
+static void zoom_in_cb(GtkToolButton *toolbutton, Display *display){
+    display->image->zoom(0.5);
+}
+
+static void false_color_cb(GtkToolButton *toolbutton, struct  update_cb *){
+    GtkWidget *label, *legend_box, *legend_image;
+    char text[NAME_MAX+1];
+    int i;
+    /*
+     
+     if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toolbutton))){
+     color_map=false_colors;
+     legend_surface=cairo_image_surface_create_for_data ((uchar*)false_colors, CAIRO_FORMAT_RGB24, 256, 1, 256*4);
+     legend=gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
+     legend_image = gtk_image_new_from_surface(legend_surface);
+     legend_box=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1);
+     
+     FOR(i, 7)
+     {
+     SPRINTF(text, "%d", (i+1)*256/8);
+     label = gtk_label_new(text);
+     gtk_box_pack_start(GTK_BOX(legend_box), label, TRUE, TRUE, 0);
+     }
+     gtk_container_add(GTK_CONTAINER(legend), legend_image);
+     gtk_container_add(GTK_CONTAINER(legend), legend_box);
+     gtk_paned_add2(GTK_PANED(paned),legend );
+     gtk_widget_show_all(legend);
+     }
+     else{
+     color_map=gray_colors;
+     if (legend){
+     gtk_container_remove(GTK_CONTAINER(paned), legend);
+     gtk_widget_destroy(legend);
+     legend=NULL;
+     }
+     }*/
+}
+
+static  gboolean frame_rate(gpointer user_data){
+    auto display = reinterpret_cast<Display*>(user_data);
+    display->framerate();
+    return G_SOURCE_CONTINUE;
+}
+
+Display::Display(blc_array const &array):blc_array(array){
+    uint32_t type_string,format_string;
+    char label_text[NAME_MAX + 1];
+    
+    switch (format){
+        case 'Y800':switch (type){
+            case 'UIN8':image=make_unique<UIN8_Y800_image>(*this); //*this because it is a copy constructor of blc_array
+                break;
+            }
+            break;
+        case 'yuv2':case 'UYVY':
+            switch (type){
+          //      case 'UIN8':image=make_unique<UIN8_UYVY_display>(*this);
+                    break;
+            }
+            break;
+        default:
+            EXIT_ON_ERROR("Unknow image type or format");
+    }
+    
+    widget=gtk_box_new(GTK_ORIENTATION_VERTICAL, 3);
+    toolbar = gtk_toolbar_new();
+    general_statusbar=gtk_statusbar_new();
+    framerate_statusbar=gtk_statusbar_new();
+    pointer_statusbar=gtk_statusbar_new();
+    
+    gtk_container_add(GTK_CONTAINER(general_statusbar), framerate_statusbar);
+    gtk_container_add(GTK_CONTAINER(general_statusbar), pointer_statusbar);
+    gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH);
+    
+    g_timeout_add(1000, frame_rate, this);
+    
+    SPRINTF(label_text, "%.4s, %.4s, %zxx%zu", UINT32_TO_STRING(type_string, type), UINT32_TO_STRING(format_string, format), image->width, image->height);
+    gtk_statusbar_push(GTK_STATUSBAR(general_statusbar), 0, label_text);
+
+    blgtk_add_tool_button(toolbar, "zoom\n1:1", "zoom-original", G_CALLBACK(zoom_original_cb), this);
+    blgtk_add_tool_button(toolbar, "zoom\nx0.5", "zoom-out", G_CALLBACK(zoom_out_cb), this);
+    blgtk_add_tool_button(toolbar, "zoom\nx2", "zoom-in", G_CALLBACK(zoom_in_cb),  this);
+    fit_button=GTK_WIDGET(blgtk_add_toggle_tool_button(toolbar, "zoom\nfit", "zoom-fit-best", nullptr, nullptr));
+    gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(fit_button), TRUE);
+    //    blgtk_add_toggle_tool_button(toolbar, "cursor\nvalues", "system-search", NULL, NULL);/
+ //   histogram_button=GTK_WIDGET(blgtk_add_toggle_tool_button(toolbar, "histogram", "x-office-presentation", G_CALLBACK(histogram_cb), this));
+    
+// GtkWidget *paned=gtk_paned_new(GTK_ORIENTATION_VERTICAL);
+//    gtk_paned_set_wide_handle (GTK_PANED(paned), 1);
+    
+ //   gtk_paned_add1(GTK_PANED(paned), image->widget);
+  //  gtk_paned_add2(GTK_PANED(paned), gtk_label_new("bottom"));
+
+    gtk_container_add(GTK_CONTAINER(widget), toolbar);
+    gtk_container_add(GTK_CONTAINER(widget), image->widget);
+    //gtk_container_add(GTK_CONTAINER(widget), paned);
+    gtk_container_add(GTK_CONTAINER(widget), general_statusbar);
+    
+//    image->zoom_original();
+    gtk_widget_show_all(widget);
+
+  //  g_signal_connect(G_OBJECT(widget), "size-allocate", G_CALLBACK(Display::resize_cb), this);
+
+   /*  gtk_widget_set_vexpand(image, TRUE);
+     gtk_widget_set_hexpand(image, TRUE);*/
+    
+    /*g_signal_connect(window, "window-state-event", G_CALLBACK (toggle_fullscreen), this);
+    if (fullscreen_option) gtk_window_fullscreen(GTK_WINDOW(window));*/
+}
+
+Display::~Display(){
+    data=nullptr;
+}
+
+void Display::update(){
+    if (image) {
+        image->update();
+        gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, image->pointer_info.data());
+    }
+    
+    if (histogram) {
+        histogram->update();
+        gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, histogram->pointer_info.data());
+    }
+    iterations_nb++;
+}
+
+void Display::histogram_cb(){
+    
+}
+
+void Display::framerate(){
+    char text[64];
+    
+    SPRINTF(text, "%dHz", iterations_nb);
+    gtk_statusbar_push(GTK_STATUSBAR(framerate_statusbar), 0, text);
+    iterations_nb=0;
+}
diff --git a/src/Image.cpp b/src/Image.cpp
new file mode 100644
index 0000000..cd4a375
--- /dev/null
+++ b/src/Image.cpp
@@ -0,0 +1,92 @@
+
+//
+//  Display.cpp
+//  c_gtk_image
+//
+//  Created by Arnaud Blanchard on 12/11/2020.
+//
+#include "Image.hpp"
+
+Image::Image(blc_array const &array):blc_array(array){    
+    widget=gtk_scrolled_window_new(nullptr, nullptr);
+ //   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget), GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS);
+    GdkDisplay* default_display = gdk_display_get_default();
+    GdkSeat* seat = gdk_display_get_default_seat(default_display);
+    pointer_device = gdk_seat_get_pointer(seat);
+    window=gtk_widget_get_window(widget);
+
+    gtk_widget_set_vexpand(GTK_WIDGET(widget), TRUE);
+    image = gtk_image_new();
+    gtk_container_add(GTK_CONTAINER(widget), image );
+    
+    
+
+//    GtkWidget* event_box=gtk_event_box_new();
+  //  gtk_container_add(GTK_CONTAINER(event_box), image);
+  //  gtk_container_add(GTK_CONTAINER(widget), event_box );
+
+//    gtk_widget_set_vexpand(widget, TRUE);
+//    gtk_widget_set_size_request(widget, width, height);
+    
+    g_signal_connect(G_OBJECT(image), "size-allocate", G_CALLBACK(resize_cb), this);
+}
+
+Image::~Image(){
+    data=nullptr; //To avoid to free the data twice here, and by the calling array;
+}
+
+bool Image::get_pointer_position(int &i, int &j){
+    int x, y;
+    gdk_window_get_device_position(window, pointer_device, &x, &y, nullptr);
+    i=x*x_scale;
+    j=y*y_scale;
+    
+    return (i >=0 && i<width && j>=0 && j<height);
+}
+
+void Image::active_fit_size(){
+    fit_size=true;
+  //  cairo_surface_set_device_scale(surface, (double)width/allocation->width, (double)height/(double)allocation->height);
+}
+
+void Image::zoom_original(){
+    fit_size=false;
+    x_scale=1;
+    y_scale=1;
+    cairo_surface_set_device_scale(surface, x_scale, y_scale);
+    gtk_widget_set_size_request(widget, width/x_scale, height/y_scale);
+
+  //  auto image_height=gtk_widget_get_allocated_height(image);
+   // auto window_height = gtk_widget_get_allocated_height(image);
+    
+   // if (fit_size) gtk_window_resize(GTK_WINDOW(image), width, window_height-image_height+height );*/
+}
+
+void Image::zoom(double factor){
+    fit_size=false;
+    x_scale*=factor;
+    y_scale*=factor;
+    cairo_surface_set_device_scale(surface, x_scale, y_scale);
+    
+    gtk_widget_set_size_request(widget, width/x_scale, height/y_scale);
+  /*  auto image_height=gtk_widget_get_allocated_height(image);
+    auto image_width=gtk_widget_get_allocated_width(image);*/
+
+
+//    gtk_widget_size_request(GTK_WINDOW(image), width, height );
+
+}
+
+void Image::resize(int allocated_width, int allocated_height){
+    x_scale=double(width)/double(allocated_width);
+    y_scale=double(height)/double(allocated_height);
+    cairo_surface_set_device_scale(surface, x_scale, y_scale);
+}
+
+// static
+void Image::resize_cb(GtkWidget *widget,  GdkRectangle *allocation, Image *image){
+    if (image->fit_size) image->resize(allocation->width, allocation->height);
+
+ 
+    //  else  if (display->legend_surface) cairo_surface_set_device_scale(display->legend_surface, 256/(double)allocation->width, 1/(double)allocation->height);
+}
diff --git a/src/UIN8_UYVY.cpp b/src/UIN8_UYVY.cpp
new file mode 100644
index 0000000..8a6d3ba
--- /dev/null
+++ b/src/UIN8_UYVY.cpp
@@ -0,0 +1,82 @@
+#include "UIN8_UYVY.hpp"
+
+UIN8_UYVY_display::UIN8_UYVY_display(blc_array const &array):Display(array){
+    int Y, Cb, Cr;
+    int i, j;
+    uint8_t  G_tmp, B;
+    uint8_t  R[256];
+    
+    width=dims[1].length;
+    height=dims[2].length;
+    image_surface=cairo_image_surface_create( CAIRO_FORMAT_RGB24, width, height);
+    
+    if (RGBA_from_YUYV==nullptr){
+        RGBA_from_YUYV= MANY_ALLOCATIONS(256*256*256, uint32_t);
+        FOR(Y, 256){
+            FOR(j,256) R[j]= CLIP_UCHAR(Y+1.13983*(j-128)); //It does not depend on Cb
+            FOR(Cb, 256)
+            {
+                B =  CLIP_UCHAR(Y+2.03211*(Cb-128)); // It does not depend on Cr
+                G_tmp = - 0.58060*(Cb-128);
+                
+                FOR(Cr, 256){
+                    i = Y + (Cb << 8) + (Cr << 16);
+                    // Version Wikipedia
+                    RGBA_from_YUYV[i] = (R[Cr]<<16) + (((uchar)CLIP_UCHAR(Y-0.39465*(Cr-128) + G_tmp))<< 8)+ B + (255<<24);
+                }
+            }
+        }
+    }
+}
+
+void UIN8_UYVY_display::update(){
+    int Y, U = 128, V = 128, j;
+    //uints32 is the array data, surface_data is the data of the image
+    uint32_t* tmp_pixels=reinterpret_cast<uint32_t*>(image_data);
+    for (int i=0; i!=size;){
+           U = uchars[i++];
+           Y = uchars[i++];
+           j = Y + (U << 8) + (V << 16);
+           tmp_pixels[0]=RGBA_from_YUYV[j];
+    
+           V = uchars[i++];
+           Y = uchars[i++];
+           j = Y + (U << 8) + (V << 16);
+           tmp_pixels[1]=RGBA_from_YUYV[j];
+           tmp_pixels+=2;
+    }
+}
+
+
+void UIN8_UYVY_display::snprint_pixel(char *text, int size_max, int i, int j){
+    uint8_t Y, U, V;
+    Y=uchars[i*2+j*width*2+1];
+    U=uchars[i*4+j*width*2];
+    V=uchars[i*4+j*width*2+2];
+    snprintf(text, size_max, "%d,%d: Y:%d, U:%d, V:%d", i, j, Y, U, V);
+}
+
+void UIN8_UYVY_display::snprint_histogram(char *text, int size_max, int i){
+    uint8_t Y, U, V;
+}
+
+
+void UIN8_UYVY_display::show_histogram(){
+    
+
+}
+
+void UIN8_UYVY_display::hide_histogram(){
+
+}
+
+uint32_t *UIN8_UYVY_display::RGBA_from_YUYV = nullptr;
+
+
+void UIN8_UYVY_histogram::snprint(char *text, int size_max, int i, int j){
+    uint8_t Y, U, V;
+    Y=array->uchars[i*2+j*width*2+1];
+    U=array->uchars[i*4+j*width*2];
+    V=array->uchars[i*4+j*width*2+2];
+    SPRINTF(text, "Y[%d]=%f.2%%", x, histogram[x]*100/(float)array->size);
+}
diff --git a/src/UIN8_Y800.cpp b/src/UIN8_Y800.cpp
new file mode 100644
index 0000000..03d0907
--- /dev/null
+++ b/src/UIN8_Y800.cpp
@@ -0,0 +1,71 @@
+#include "UIN8_Y800.hpp"
+#include <blc_image.h>
+
+using namespace std;
+
+#define Y800_to_RGBA(val) (val+(val<<8)+(val<<16)+(255<<24))
+
+UIN8_Y800_image::UIN8_Y800_image(blc_array const &array):Image(array){
+    width=dims[0].length;
+    height=dims[1].length;
+    surface=cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
+    pixels=reinterpret_cast<uint32_t*>(cairo_image_surface_get_data(surface));
+    gtk_image_set_from_surface(GTK_IMAGE(image), surface);
+    
+    color_map.resize(256);
+    for (int i=0; i!=color_map.size(); i++) color_map[i]=Y800_to_RGBA(i);
+    
+    //   false_colors_button=blgtk_add_toggle_tool_button(toolbar, "false\ncolors", "applications-graphics", G_CALLBACK(false_color_cb), nullptr);
+}
+
+void UIN8_Y800_image::update_pointer_info(){
+    int i, j;
+    if (get_pointer_position(i, j)){
+        pointer_info=to_string(i)+","+to_string(j)+" Y:"+to_string(uchars[i+j*width]);
+    }
+}
+
+void UIN8_Y800_image::update(){
+    uint8_t grey;
+
+    for (int i=0; i<total_length; i++) {
+        grey = uchars[i];
+        pixels[i]=color_map[grey];
+    }
+    gtk_widget_queue_draw(image);
+}
+
+
+
+
+
+// *** Histogram ***
+
+UIN8_Y800_histogram::UIN8_Y800_histogram(blc_array const &array):Histogram(array){
+    surface=cairo_image_surface_create(CAIRO_FORMAT_RGB24, 256, 256);
+    pixels=reinterpret_cast<uint32_t*>(cairo_image_surface_get_data(surface));
+}
+
+void UIN8_Y800_histogram::update(){
+    int i, j;
+    uint8_t grey, hist_max;
+    FOR(i, 256) {
+        grey=uchars[i];
+        histogram[grey]++;
+    }
+    hist_max = *max_element(uchars, uchars+size);
+    FOR(i, 256){
+        FOR(j, histogram[i]*255/hist_max) pixels[(255-j)*256+i]=color_map[i];
+        pixels[(255-histogram[i]*255/hist_max)*256+i]=255<<24;
+        FOR(j, 255-histogram[i]*255/hist_max) pixels[j*256+i]=0;
+    }
+    gtk_widget_queue_draw(image);
+}
+
+void UIN8_Y800_histogram::update_pointer_info(){
+    int i;
+    
+    if (get_pointer_position(i)){
+        pointer_info="Y["+to_string(i)+"]="+to_string(histogram[i]*100/static_cast<float>(size))+"%";
+    }
+}
diff --git a/src/histogram.cpp b/src/histogram.cpp
index 76871ee..4dfc385 100644
--- a/src/histogram.cpp
+++ b/src/histogram.cpp
@@ -1,220 +1,34 @@
-#include "common.h"
+#include "Histogram.hpp"
+#include <vector>
 
-#include <fcntl.h> // O_RDONLY ...
-#include <stdio.h>
-#include <gtk/gtk.h>
-#include <string.h>
-#include <stdint.h> //uint32_t
-#include <jpeglib.h>
-#include <sys/mman.h>
-#include <errno.h> //errno
-#include "blc_image.h"
-#include "blc_realtime.h"
-#include "blc_program.h" //blc_status
+using namespace std;
 
-//cairo_surface_t *legend_surface;
-guint histogram_tick_id;
-uint32_t *histogram_data=NULL;
-GtkWidget *histogram_image, *histogram, *histogram_scrolled_window;
-cairo_surface_t *histogram_surface;
 
-static void draw_histogram(uint32_t *drawing, int height, int histogram[256], uint32_t color_map[256], int max){
-    int i, j, val;
-    FOR_INV(i, 256)
-    {
-        val = height-histogram[i]*(height-1)/max;
-        for(j=height; j!=val; j--) drawing[j*256+i]=color_map[i];
-        drawing[val*256+i]=255<<24;
-        FOR_INV(j, val) drawing[j*256+i]=0;
-    }
-}
-
-static gboolean update_histogram_cb(GtkImage *image, GdkFrameClock *, gpointer pointer_statusbar){
-    int i, j, x, y;
-    double sx, sy;
-    int histogram[256]={0}, histogram1[256]={0}, histogram2[256]={0}, hist_max=0, max1=0, max2=0;
-    char text[64];
-    struct timeval timer;
-    
-    CLEAR(timer);
-    gdk_window_get_device_position(gtk_widget_get_window(GTK_WIDGET(image)), pointer_device, &x, &y, NULL);
-    cairo_surface_get_device_scale(histogram_surface, &sx, &sy);
+Histogram::Histogram(blc_array const &array):blc_array(array){
+    pointer_info.resize(64);
+    widget=gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
+    GdkDisplay* default_display = gdk_display_get_default();
+    GdkSeat* seat = gdk_display_get_default_seat(default_display);
+    pointer_device = gdk_seat_get_pointer(seat);
+    window=gtk_widget_get_window(widget);
     
-    x*=sx;
-    y*=sy;
-    
-    switch (array->format){
-        case 'Y800':
-            switch (array->type){
-                case 'UIN8':
-                    FOR(i, array->size) histogram[array->uchars[i]]++;
-                    FOR(i, 256) hist_max=MAX(histogram[i], hist_max);
-                    
-                    FOR(i, 256)
-                {
-                    FOR_INV(j, histogram[i]*255/hist_max) histogram_data[(255-j)*256+i]=color_map[i];
-                    histogram_data[(255-histogram[i]*255/hist_max)*256+i]=255<<24;
-                    FOR_INV(j, 255-histogram[i]*255/hist_max) histogram_data[j*256+i]=0;
-                }
-                    if (y>=0 && y<256 && x >=0 && x<256)
-                    {
-                        SPRINTF(text, "Y[%d]=%f.2%%", x, histogram[x]*100/(float)array->size);
-                        gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, text);
-                    }
-                    break;
-                case 'FL32':
-                    FOR(i, array->total_length) histogram[(int)CLIP_UCHAR(((array->floats[i]-min_val)/(max_val-min_val)*256+0.5))]++;
-                    FOR(i, 256) hist_max=MAX(histogram[i], hist_max);
-                    FOR(i, 256)
-                {
-                    FOR_INV(j, histogram[i]*255/hist_max) histogram_data[(255-j)*256+i]=color_map[i];
-                    histogram_data[(255-histogram[i]*255/hist_max)*256+i]=255<<24;
-                    FOR_INV(j, 255-histogram[i]*255/hist_max) histogram_data[j*256+i]=0;
-                }
-                    if (y>=0 && y<256 && x >=0 && x<256)
-                    {
-                        SPRINTF(text, "Y[%f]=%f.2%%", x*(max_val-min_val-0.5)/256+min_val, histogram[x]*100/(float)array->total_length);
-                        gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, text);
-                    }
-                    break;
-                default: EXIT_ON_ARRAY_ERROR( array, "Type not managed for format 'Y800'. Only 'UIN8' is");
-            }
-            break;
-        case 'RGB3':
-            FOR_INV(i, array->size/3){
-                histogram[array->uchars[i*3]]++;
-                histogram1[array->uchars[i*3+1]]++;
-                histogram2[array->uchars[i*3+2]]++;
-            }
-            
-            FOR_INV(i, 256)
-        {
-            hist_max=MAX(histogram[i], hist_max);
-            max1=MAX(histogram1[i], max1);
-            max2=MAX(histogram2[i], max2);
-        }
-            draw_histogram(histogram_data, 85, histogram, r_colors, hist_max);
-            draw_histogram(histogram_data+256*85, 85, histogram1, g_colors, max1);
-            draw_histogram(histogram_data+256*170, 85, histogram2, b_colors, max2);
-            
-            if (y>=0 && y<256 && x >=0 && x<256)
-            {
-                if (y<85)  SPRINTF(text, "R[%d]=%f.2%%", x, histogram[x]*100*2/(float)array->size);
-                else if (y<170)  SPRINTF(text, "G[%d]=%.2f%%", x, histogram1[x]*100*4/(float)array->size);
-                else  SPRINTF(text, "B[%d]=%.2f%%", x, histogram2[x]*100*4/(float)array->size);
-                gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, text);
-            }
-            
-            break;
-        case 'YUYV':
-            for(i=array->size-2;i!=-1;i-=2) histogram[array->uchars[i]]++;
-            for(i=array->size-1;i!=-2;i-=4) histogram1[array->uchars[i]]++;
-            for(i=array->size-3;i!=-4;i-=4) histogram2[array->uchars[i]]++;
-            
-            FOR_INV(i, 256)
-        {
-            hist_max=MAX(histogram[i], hist_max);
-            max1=MAX(histogram1[i], max1);
-            max2=MAX(histogram2[i], max2);
-        }
-            
-            draw_histogram(histogram_data, 127, histogram, gray_colors, hist_max);
-            draw_histogram(histogram_data+256*128, 63, histogram1, u_colors, max1);
-            draw_histogram(histogram_data+256*192, 63, histogram2, v_colors, max2);
-            
-            if (y>=0 && y<256 && x >=0 && x<256)
-            {
-                if (y<128)  SPRINTF(text, "Y[%d]=%f.2%%", x, histogram[x]*100*2/(float)array->size);
-                else if (y<192)  SPRINTF(text, "U[%d]=%.2f%%", x, histogram1[x]*100*4/(float)array->size);
-                else  SPRINTF(text, "V[%d]=%.2f%%", x, histogram2[x]*100*4/(float)array->size);
-                gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, text);
-            }
-            break;
-        case 'yuv2':
-            for(i=array->size-1;i!=-1;i-=2) histogram[array->uchars[i]]++;
-            for(i=array->size-2;i!=-2;i-=4) histogram1[array->uchars[i]]++;
-            for(i=array->size-4;i!=-4;i-=4) histogram2[array->uchars[i]]++;
-            
-            FOR_INV(i, 256)
-        {
-            hist_max=MAX(histogram[i], hist_max);
-            max1=MAX(histogram1[i], max1);
-            max2=MAX(histogram2[i], max2);
-        }
-            
-            draw_histogram(histogram_data, 127, histogram, gray_colors, hist_max);
-            draw_histogram(histogram_data+256*128, 63, histogram1, u_colors, max1);
-            draw_histogram(histogram_data+256*192, 63, histogram2, v_colors, max2);
-            
-            if (y>=0 && y<256 && x >=0 && x<256)
-            {
-                if (y<128)  SPRINTF(text, "Y[%d]=%f.2%%", x, histogram[x]*100*2/(float)array->size);
-                else if (y<192)  SPRINTF(text, "U[%d]=%.2f%%", x, histogram1[x]*100*4/(float)array->size);
-                else  SPRINTF(text, "V[%d]=%.2f%%", x, histogram2[x]*100*4/(float)array->size);
-                gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, text);
-                
-            }
-            break;
-            
-        default:
-            gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, "No histogram for this format. Only Y800,RGB3 or yuv2.");
-            
-    }
-    gtk_image_set_from_surface(image, histogram_surface);
-    return G_SOURCE_CONTINUE;
+    image=gtk_image_new_from_surface(surface);
+    scrolled_window=gtk_scrolled_window_new(nullptr, nullptr);
+    legend_box=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1);
+    gtk_container_add(GTK_CONTAINER(scrolled_window), image);
+    g_signal_connect(G_OBJECT(scrolled_window), "size-allocate", G_CALLBACK(resize_cb), this);
 }
 
-
-static void histogram_resize_cb(GtkWidget *widget,  GdkRectangle *allocation, gpointer user_data)
-{
-    (void) widget;
-    (void) user_data;
-    cairo_surface_set_device_scale(histogram_surface, (double)256/(double)allocation->width, (double)256/(double)allocation->height);
+bool Histogram::get_pointer_position(int &i){
+    int j, x,y;
+    gdk_window_get_device_position(window, pointer_device, &x, &y, nullptr);
+    i=x*x_scale;
+    j=y*y_scale;
+    return (i>=0 && i<256 && j>=0 && j<256);
 }
 
-void histogram_cb(GtkToolButton *toolbutton, gpointer pointer_statusbar )
-{
-    char text[NAME_MAX];
-    GtkWidget *legend_box, *label;
+// static
+void Histogram::resize_cb(GtkWidget *widget, GdkRectangle *allocation, Histogram *histogram){
     
-    if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toolbutton)))
-    {
-        histogram_data=MANY_ALLOCATIONS(256*256, uint32_t);
-        histogram_surface=cairo_image_surface_create_for_data ((uchar*)histogram_data, CAIRO_FORMAT_ARGB32, 256, 256, 256*4);
-        histogram_image=gtk_image_new_from_surface(histogram_surface);
-        histogram_scrolled_window=gtk_scrolled_window_new(NULL, NULL);
-        histogram=gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
-        legend_box=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1);
-        gtk_container_add(GTK_CONTAINER(histogram_scrolled_window), histogram_image);
-        gtk_container_add(GTK_CONTAINER(histogram), histogram_scrolled_window);
-        gtk_container_add(GTK_CONTAINER(histogram), legend_box);
-        
-        SPRINTF(text, "%f", min_val);
-        label=gtk_label_new(text);
-        gtk_box_pack_start(GTK_BOX(legend_box), label, FALSE, FALSE, 0);
-        SPRINTF(text, "%f", max_val);
-        label=gtk_label_new(text);
-        gtk_box_pack_end(GTK_BOX(legend_box), label, FALSE, FALSE, 0);
-        
-        if (legend)
-        {
-            gtk_container_remove(GTK_CONTAINER(paned), legend);
-            //    gtk_widget_destroy(legend);
-            legend=NULL;
-        }
-        gtk_paned_add2(GTK_PANED(paned), histogram);
-        gtk_widget_set_vexpand(histogram_image, TRUE);
-        gtk_paned_set_position(GTK_PANED(paned), gtk_widget_get_allocated_height(paned)-256);
-        gtk_widget_show_all(paned);
-        g_signal_connect(G_OBJECT(histogram_scrolled_window), "size-allocate", G_CALLBACK(histogram_resize_cb), NULL);
-        histogram_tick_id = gtk_widget_add_tick_callback(GTK_WIDGET(histogram_image), (GtkTickCallback) update_histogram_cb, pointer_statusbar, NULL);
-    }
-    else
-    {
-        gtk_widget_remove_tick_callback(GTK_WIDGET(histogram_image), histogram_tick_id);
-        gtk_container_remove(GTK_CONTAINER(paned), histogram);
-        FREE(histogram_data);
-        //    gtk_widget_destroy(histogram);
-        histogram=NULL;
-    }
+    cairo_surface_set_device_scale(histogram->surface, (double)256/(double)allocation->width, (double)256/(double)allocation->height);
 }
diff --git a/src/image_display.cpp b/src/image_display.cpp
deleted file mode 100644
index c523d33..0000000
--- a/src/image_display.cpp
+++ /dev/null
@@ -1,637 +0,0 @@
-#include "common.h"
-
-#include <fcntl.h> // O_RDONLY ...
-#include <stdio.h>
-#include <gtk/gtk.h>
-#include <string.h>
-#include <stdint.h> //uint32_t
-#include <jpeglib.h>
-#include <sys/mman.h>
-#include <errno.h> //errno
-#include "blc_image.h"
-#include "blc_realtime.h"
-#include "blc_program.h" //blc_status
-
-uint32_t false_colors[256];
-uint32_t gray_colors[256];
-uint32_t u_colors[256];
-uint32_t v_colors[256];
-uint32_t r_colors[256];
-uint32_t g_colors[256];
-uint32_t b_colors[256];
-uint32_t *color_map;
-
-int iterations=0;
-int colors_initilized = 0;
-
-int mouse_image_x, mouse_image_y;
-
-cairo_surface_t *legend_surface, *image_surface;
-gulong pointer_value_handler;
-GtkWidget *paned;
-
-static uint32_t *RGBA_from_YUYV = NULL;
-
-static GtkWidget   *vbox,  *scrolled_window, *event_box, *toolbar,  *general_statusbar;
-static GtkToggleToolButton *zoom_button, *false_colors_button, *histogram_button;
-GtkWidget *image;
-
-static uchar *image_buffer;
-
-static pthread_t init_table_thread;
-double zoom_in_factor=2, zoom_out_factor=0.5;
-
-GtkWidget *legend=NULL;
-
-static int width=0, height=0;
-blc_array *array=NULL;
-
-static int g_source_continue=G_SOURCE_CONTINUE;
-
-#define SWAP_RGBA_TO_CAIRO_ARGB32(x) ((((x) & 0x000000FF) << 16) | (((x) & 0x00FF0000) >> 16) |  ((x) & 0xFF00FF00) )
-
-
-static GtkToggleToolButton *blgtk_add_toggle_tool_button(GtkWidget *toolbar, char const *label, char const *icon_name, GCallback callback, void *user_data)
-{
-    GtkToggleToolButton *toggle_tool_button;
-    
-    toggle_tool_button=GTK_TOGGLE_TOOL_BUTTON(gtk_toggle_tool_button_new());
-    
-    gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toggle_tool_button), icon_name);
-    gtk_tool_button_set_label(GTK_TOOL_BUTTON(toggle_tool_button), label);
-    gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(toggle_tool_button));
-    if (callback) g_signal_connect(G_OBJECT(toggle_tool_button), "clicked", callback, user_data);
-    return toggle_tool_button;
-}
-
-static GtkWidget *blgtk_add_tool_button(GtkWidget *toolbar, gchar const *label, gchar const *icon_name, GCallback callback, gpointer user_data)
-{
-    GtkWidget *tool_button;
-    
-    tool_button=GTK_WIDGET(gtk_tool_button_new(gtk_image_new_from_icon_name(icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR), label));
-    gtk_container_add(GTK_CONTAINER(toolbar), tool_button);
-    if (callback) g_signal_connect(G_OBJECT(tool_button), "clicked", callback, user_data);
-    return tool_button;
-}
-
-static void toggle_fullscreen(GtkWidget *widget, GdkEventWindowState *event, gpointer   user_data){
-    
-    if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN ){
-        gtk_widget_hide(toolbar);
-        gtk_widget_hide(general_statusbar);
-    }
-    else{
-        gtk_widget_show(toolbar);
-        gtk_widget_show(general_statusbar);
-    }
-}
-
-void* create_RGBA_from_YUYV(void *widget)
-{
-    int Y, Cb, Cr;
-    int i, j;
-    float G_tmp;
-    static uchar  R[256], B;
-    (void)widget;
-    
-    if (RGBA_from_YUYV == NULL){
-        RGBA_from_YUYV= MANY_ALLOCATIONS(256*256*256, uint32_t);
-        FOR_INV(Y, 256){
-            FOR_INV(j,256) R[j]= CLIP_UCHAR(Y+1.13983*(j-128)); //It does not depend on Cb
-            FOR_INV(Cb, 256)
-            {
-                B =  CLIP_UCHAR(Y+2.03211*(Cb-128)); // It does not depend on Cr
-                G_tmp = - 0.58060*(Cb-128);
-                
-                FOR_INV(Cr, 256)
-                {
-                    i = Y + (Cb << 8) + (Cr << 16);
-                    
-                    // Version Wikipedia
-                    RGBA_from_YUYV[i] = (R[Cr]<<16) + (((uchar)CLIP_UCHAR(Y-0.39465*(Cr-128) + G_tmp))<< 8)+ B + (255<<24);
-                }
-            }
-        }
-    }
-    return NULL;
-}
-
-void init_false_colors()
-{
-    int i, val;
-    int min;
-    
-    FOR_INV(i, 256)
-    {
-        min=(int)(i*6/256)*256/6;
-        if ((i-min) > 256/6) min++;
-        val=(i-min)*6;
-        switch (i*6/256)
-        {
-            case 0:false_colors[i]=(val)+(255<<24);
-                break;
-            case 1:false_colors[i]=255+(val<<8)+(255<<24);
-                break;
-            case 2:false_colors[i]=(255-val)+(255<<8)+(255<<24);
-                break;
-            case 3:false_colors[i]=(255<<8)+(val<<16)+(255<<24);
-                break;
-            case 4:false_colors[i]=((255-val)<<8)+(255<<16)+(255<<24);
-                break;
-            case 5:false_colors[i]=(255<<16)+(val)+(val<<8)+(255<<24);
-                break;
-        }
-    }
-}
-
-static void init_color_maps(){
-    int i;
-    if (RGBA_from_YUYV==NULL) create_RGBA_from_YUYV(NULL);
-    
-    FOR_INV(i, 256){
-        gray_colors[i]= i+(i<<8)+(i<<16)+(255<<24);
-        u_colors[i]=  RGBA_from_YUYV[128+(i<<8)+(128<<16)]+(255<<24);
-        v_colors[i]=RGBA_from_YUYV[127+(127<<8)+(i<<16)];
-        r_colors[i]= 128+(128<<8)+(i<<16)+(255<<24);
-        g_colors[i]=  128+(i<<8)+(128<<16)+(255<<24);
-        b_colors[i]=  i+(128<<8)+(128<<16)+(255<<24);
-    }
-    init_false_colors();
-}
-
-/** Gtk cannot display a black and white image, therefore we convert it before updating it*/
-gboolean update_Y800_image(GtkImage *image, GdkFrameClock *, gpointer pointer_statusbar)
-{
-    float gain;
-    int i, x, y, j;
-    double sx, sy;
-    uint32_t *values;
-    char text[64];
-    
-    gain=1/(max_val-min_val);
-    
-    if (blc_command_loop_start()){
-        values = (uint32_t*)image_buffer;
-        if (array->type=='UIN8')
-            FOR(i, array->size) values[i]=color_map[array->uchars[i]];
-        else if (array->type=='FL32')
-            FOR(i, array->total_length) values[i]=color_map[CLIP_UCHAR((array->floats[i]-min_val)*256*gain-0.5f)];
-        else EXIT_ON_ARRAY_ERROR(array, "Type not managed");
-        gtk_image_set_from_surface(image, image_surface);
-        
-        gdk_window_get_device_position(gtk_widget_get_window(GTK_WIDGET(image)), pointer_device, &x, &y, NULL);
-        cairo_surface_get_device_scale(image_surface, &sx, &sy);
-        i=x*sx;
-        j=y*sy;
-        if (i<width && j<height && i>=0 && j>=0){
-            if (array->type=='UIN8')  SPRINTF(text, "%4d,%4d: Y:%3d", i,j, array->uchars[i+j*width]);
-            else if (array->type=='FL32')  SPRINTF(text, "%4d,%4d: Y:%.3f", i,j, array->floats[i+j*width]);
-            gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, text);
-        }
-        iterations++;
-        blc_command_loop_end();
-    } else exit(EXIT_SUCCESS);
-    return G_SOURCE_CONTINUE;
-}
-
-gboolean update_RGB3_image(GtkImage *image, GdkFrameClock *, gpointer pointer_statusbar)
-{
-    char text[NAME_MAX];
-    double sx, sy;
-    int x, y, i=0, j=0;
-    int R, G, B;
-    
-    if (blc_command_loop_start()){
-        gdk_window_get_device_position(gtk_widget_get_window(GTK_WIDGET(image)), pointer_device, &x, &y, NULL);
-        cairo_surface_get_device_scale(image_surface, &sx, &sy);
-        
-        while(i!=array->size){
-            image_buffer[j++]=array->uchars[i+2];
-            image_buffer[j++]=array->uchars[i+1];
-            image_buffer[j++]=array->uchars[i];
-            j++;
-            i+=3;
-        }
-        
-        i=x*sx;
-        j=y*sy;
-        
-        if (i<width && j<height && i>=0 && j>=0){
-            if (mouse_array.dims!=nullptr)
-            {
-                mouse_array.uints32[0]=i;
-                mouse_array.uints32[1]=j;
-                gdk_device_get_state(pointer_device, gtk_widget_get_window(GTK_WIDGET(image)), NULL, (GdkModifierType*)(&mouse_array.uints32[2]));
-            }
-            
-            R=array->uchars[i*4+j*width*3];
-            G=array->uchars[i*4+j*width*3+1];
-            B=array->uchars[i*4+j*width*3+2];
-            SPRINTF(text, "%4d,%4d : R:%3d,G:%3d,B:%3d", i, j, R, G, B);
-            
-            gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, text);
-        }
-        gtk_image_set_from_surface(image, image_surface);
-        iterations++;
-        blc_command_loop_end();
-    }
-    else exit (EXIT_SUCCESS);
-    return G_SOURCE_CONTINUE;
-    
-}
-
-gboolean update_RGBA_image(GtkImage *image, GdkFrameClock *, gpointer pointer_statusbar)
-{
-    double sx, sy;
-    int x, y;
-    int i, j=0;
-    char text[NAME_MAX];
-    int R, G, B, A;
-    
-    if (blc_command_loop_start()==0) exit(0);
-    gdk_window_get_device_position(gtk_widget_get_window(GTK_WIDGET(image)), pointer_device, &x, &y, NULL);
-    cairo_surface_get_device_scale(image_surface, &sx, &sy);
-    
-    FOR_INV(i, array->size/4) ((uint32_t*)image_buffer)[i]=SWAP_RGBA_TO_CAIRO_ARGB32(array->uints32[i]);
-    
-    i=x*sx;
-    j=y*sy;
-    
-    if (i<width && j<height && i>=0 && j>=0){
-        if (mouse_array.dims!=nullptr){
-            mouse_array.uints32[0]=i;
-            mouse_array.uints32[1]=j;
-            gdk_device_get_state(pointer_device, gtk_widget_get_window(GTK_WIDGET(image)), NULL, (GdkModifierType*)(&mouse_array.uints32[2]));
-        }
-        
-        R=array->uchars[i*4+j*width*4];
-        G=array->uchars[i*4+j*width*4+1];
-        B=array->uchars[i*4+j*width*4+2];
-        A=array->uchars[i*4+j*width*4+3];
-        SPRINTF(text, "%4d,%4d: R:%d G:%d B:%d A:%d", i, j, R, G, B, A);
-        
-        gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, text);
-    }
-    gtk_image_set_from_surface(image, image_surface);
-    blc_command_loop_end();
-    iterations++;
-    return G_SOURCE_CONTINUE;
-}
-
-
-// Soon we should the implementation of blc_image */
-void jpeg_error(j_common_ptr cinfo, int msg_level)
-{
-    (void) msg_level;
-    cinfo->err->num_warnings++;
-}
-
-gboolean update_JPEG_image(GtkImage *image, GdkFrameClock *frame_clock, blc_array *array)
-{
-    JSAMPROW row_pt[1];
-    GdkPixbuf *pixbuf;
-    int row_stride;
-    uchar *pixels;
-    struct jpeg_decompress_struct cinfo;
-    struct jpeg_error_mgr jerr;
-    (void) frame_clock;
-    
-    if (blc_command_loop_start()==0) exit(0);
-    
-    
-    pixbuf = gtk_image_get_pixbuf(image);
-    pixels = gdk_pixbuf_get_pixels(pixbuf);
-    
-    cinfo.err = jpeg_std_error(&jerr);
-    cinfo.err->emit_message = jpeg_error;
-    jpeg_create_decompress(&cinfo);
-    jpeg_mem_src(&cinfo, (uchar*) array->data, array->size);
-    jpeg_read_header(&cinfo, TRUE);
-    jpeg_start_decompress(&cinfo);
-    row_stride = cinfo.output_width * cinfo.output_components;
-    row_pt[0] = pixels;
-    
-    while (cinfo.output_scanline < cinfo.output_height)
-    {
-        jpeg_read_scanlines(&cinfo, row_pt, 1);
-        row_pt[0] += row_stride;
-    }
-    
-    if (cinfo.err->num_warnings != 0)
-    {
-        PRINT_WARNING("Drop image : %s", cinfo.err->jpeg_message_table[cinfo.err->last_jpeg_message]);
-        cinfo.err->num_warnings = 0;
-    }
-    gtk_image_set_from_pixbuf(image, pixbuf);
-    jpeg_finish_decompress(&cinfo);
-    jpeg_destroy_decompress(&cinfo);
-    
-    blc_command_loop_end();
-    
-    
-    return G_SOURCE_CONTINUE;
-}
-
-// 4:2:2
-
-gboolean update_YUYV_image(GtkImage *image, GdkFrameClock *frame_clock, void*)
-{
-    int i, j;
-    uchar *data = (uchar*) array->data;
-    uint32_t *pixels;
-    int Y, Cb = 128, Cr = 128;
-    
-    (void) frame_clock;
-    
-    if (blc_command_loop_start()==0) exit(0);
-    
-    pixels = (uint32_t*)image_buffer;
-    i = 0;
-    while (i != (int) array->size)
-    {
-        Y = data[i++];
-        Cb = data[i++];
-        j = Y + (Cb << 8) + (Cr << 16);
-        pixels[0]=RGBA_from_YUYV[j];
-        
-        Y = data[i++];
-        Cr = data[i++];
-        j = Y + (Cb << 8) + (Cr << 16);
-        pixels[1]=RGBA_from_YUYV[j];
-        pixels+=2;
-    }
-    iterations++;
-    gtk_image_set_from_surface(image, image_surface);
-    blc_command_loop_end();
-    
-    return G_SOURCE_CONTINUE;
-}
-
-// 4:2:2
-
-gboolean update_yuv2_image(GtkImage *image, GdkFrameClock *, gpointer pointer_statusbar)
-{
-    double sx, sy;
-    int x, y;
-    int i, j;
-    
-    uchar *data = (uchar*) array->data;
-    char text[NAME_MAX];
-    uint32_t *tmp_pixels;
-    int Y, U = 128, V = 128;
-    
-    if (blc_command_loop_start()==0) exit(0);
-    
-    tmp_pixels = (uint32_t*)image_buffer;
-    i = 0;
-    
-    while (i != (int) array->size)
-    {
-        U = data[i++];
-        Y = data[i++];
-        
-        j = Y + (U << 8) + (V << 16);
-        tmp_pixels[0]=RGBA_from_YUYV[j];
-        
-        V = data[i++];
-        Y = data[i++];
-        
-        j = Y + (U << 8) + (V << 16);
-        tmp_pixels[1]=RGBA_from_YUYV[j];
-        tmp_pixels+=2;
-    }
-    
-    gdk_window_get_device_position(gtk_widget_get_window(GTK_WIDGET(image)), pointer_device, &x, &y, NULL);
-    cairo_surface_get_device_scale(image_surface, &sx, &sy);
-    
-    i=x*sx;
-    j=y*sy;
-    
-    
-    if (i<width && j<height && i>=0 && j>=0)
-    {
-        if (mouse_array.dims!=nullptr)
-        {
-            mouse_array.uints32[0]=i;
-            mouse_array.uints32[1]=j;
-            gdk_device_get_state(pointer_device, gtk_widget_get_window(GTK_WIDGET(image)), NULL, (GdkModifierType*)(&mouse_array.uints32[2]));
-        }
-        
-        Y=array->uchars[i*2+j*width*2+1];
-        U=array->uchars[i*4+j*width*2];
-        V=array->uchars[i*4+j*width*2+2];
-        SPRINTF(text, "%d,%d: Y:%d, U:%d, V:%d", i, j, Y, U, V);
-        
-        gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, text);
-    }
-    gtk_image_set_from_surface(image, image_surface);
-    iterations++;
-    
-    blc_command_loop_end();
-    return G_SOURCE_CONTINUE;
-}
-
-
-
-static void gtk_image_resize_cb(GtkWidget *image,  GdkRectangle *allocation, gpointer user_data)
-{
-    (void) image;
-    (void) user_data;
-    
-    if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(zoom_button))) cairo_surface_set_device_scale(image_surface, (double)width/(double)allocation->width, (double)height/(double)allocation->height);
-    if (legend) cairo_surface_set_device_scale(legend_surface, 256/(double)allocation->width, 1/(double)allocation->height);
-}
-
-
-
-static void zoom_original_cb(GtkToolButton *, gpointer)
-{
-    int image_height, window_height;
-    
-    image_height=gtk_widget_get_allocated_height(image);
-    window_height = gtk_widget_get_allocated_height(window);
-    
-    cairo_surface_set_device_scale(image_surface, 1, 1);
-    if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(zoom_button)))
-        gtk_window_resize(GTK_WINDOW(window), width, window_height-image_height+height );
-}
-
-static void zoom_cb(GtkToolButton *toolbutton, double *factor)
-{
-    double sx, sy;
-    (void)toolbutton;
-    
-    gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(zoom_button), FALSE);
-    cairo_surface_get_device_scale(image_surface, &sx, &sy);
-    cairo_surface_set_device_scale(image_surface, sx*(*factor), sy*(*factor));
-}
-
-
-static void false_color_cb(GtkToolButton *toolbutton, struct  update_cb *){
-    GtkWidget *label, *legend_box, *legend_image;
-    char text[NAME_MAX+1];
-    int i;
-    
-    if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toolbutton))){
-        color_map=false_colors;
-        legend_surface=cairo_image_surface_create_for_data ((uchar*)false_colors, CAIRO_FORMAT_RGB24, 256, 1, 256*4);
-        legend=gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
-        legend_image = gtk_image_new_from_surface(legend_surface);
-        legend_box=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1);
-        
-        FOR(i, 7)
-        {
-            SPRINTF(text, "%d", (i+1)*256/8);
-            label = gtk_label_new(text);
-            gtk_box_pack_start(GTK_BOX(legend_box), label, TRUE, TRUE, 0);
-        }
-        gtk_container_add(GTK_CONTAINER(legend), legend_image);
-        gtk_container_add(GTK_CONTAINER(legend), legend_box);
-        gtk_paned_add2(GTK_PANED(paned),legend );
-        gtk_widget_show_all(legend);
-    }
-    else{
-        color_map=gray_colors;
-        if (legend){
-            gtk_container_remove(GTK_CONTAINER(paned), legend);
-            gtk_widget_destroy(legend);
-            legend=NULL;
-        }
-    }
-}
-
-int frame_rate(gpointer data)
-{
-    char text[64];
-    GtkWidget *framerate_statusbar=(GtkWidget*)data;
-    
-    SPRINTF(text, "%dHz", iterations);
-    gtk_statusbar_push(GTK_STATUSBAR(framerate_statusbar), 0, text);
-    iterations=0;
-    return G_SOURCE_CONTINUE;
-}
-
-GtkWidget *create_image_display(blc_array *tmp_array)
-{
-    GtkWidget  *pointer_statusbar, *framerate_statusbar;
-    char label_text[NAME_MAX + 1];
-    uint32_t type_string, format_string;
-    
-    array=tmp_array;
-    toolbar = gtk_toolbar_new();
-    general_statusbar=gtk_statusbar_new();
-    framerate_statusbar=gtk_statusbar_new();
-    pointer_statusbar=gtk_statusbar_new();
-    
-    blc_command_loop_init(0);
-    switch (array->format)
-    {
-        case 'NDEF':
-        case 'Y800':
-            width=array->dims[0].length;
-            height=array->dims[1].length;
-            image_buffer = MANY_ALLOCATIONS(width*height * 4, uchar);
-            image_surface=cairo_image_surface_create_for_data(image_buffer, CAIRO_FORMAT_RGB24, width, height, width * 4);
-            image=gtk_image_new_from_surface(image_surface);
-            gtk_widget_add_tick_callback(image, (GtkTickCallback) update_Y800_image, pointer_statusbar, NULL);
-            false_colors_button=blgtk_add_toggle_tool_button(toolbar, "false\ncolors", "applications-graphics", G_CALLBACK(false_color_cb),  NULL);
-            break;
-        case 'BA81':
-            EXIT_ON_ARRAY_ERROR(array, "format, Not fully implemented");
-            width=array->dims[1].length;
-            height=array->dims[2].length;
-            image_buffer = MANY_ALLOCATIONS(width*height * 4, uchar);
-            image_surface=cairo_image_surface_create_for_data(image_buffer, CAIRO_FORMAT_RGB24, width, height, width * 4);
-            image=gtk_image_new_from_surface(image_surface);
-            gtk_widget_add_tick_callback(GTK_WIDGET(image), (GtkTickCallback) update_RGB3_image, &g_source_continue, NULL);
-            break;
-        case 'RGB3':
-            width=array->dims[1].length;
-            height=array->dims[2].length;
-            image_buffer = MANY_ALLOCATIONS(width*height * 4, uchar);
-            image_surface=cairo_image_surface_create_for_data(image_buffer, CAIRO_FORMAT_RGB24, width, height, width * 4);
-            image=gtk_image_new_from_surface(image_surface);
-            gtk_widget_add_tick_callback(GTK_WIDGET(image), (GtkTickCallback) update_RGB3_image, pointer_statusbar, NULL);
-            break;
-        case 'RGBA':
-            width=array->dims[1].length;
-            height=array->dims[2].length;
-            image_buffer = MANY_ALLOCATIONS(width*height * 4, uchar);
-            image_surface=cairo_image_surface_create_for_data(image_buffer, CAIRO_FORMAT_ARGB32, width, height, width * 4);
-            image=gtk_image_new_from_surface(image_surface);
-            gtk_widget_add_tick_callback(GTK_WIDGET(image), (GtkTickCallback) update_RGBA_image, pointer_statusbar, NULL);
-            break;
-            /*    case 'JPEG':
-             width=channel->lengths[1];
-             height=channel->lengths[2];
-             image_buffer = MANY_ALLOCATIONS(width*height*3, uchar);
-             pixbuf = gdk_pixbuf_new_from_data( image_buffer, GDK_COLORSPACE_RGB, 0, 8, width, height, width * 3, NULL, NULL);
-             image = gtk_image_new_from_pixbuf(pixbuf);
-             gtk_widget_add_tick_callback(image, (GtkTickCallback) update_JPEG_image, channel, NULL);*/
-        case 'YUYV':
-            width=array->dims[1].length;
-            height=array->dims[2].length;
-            image_buffer = MANY_ALLOCATIONS(width*height * 4, uchar); //In order to be sure that the rowstride will be 'gui.width * 3'
-            image_surface=cairo_image_surface_create_for_data(image_buffer, CAIRO_FORMAT_RGB24, width, height, width * 4);
-            image=gtk_image_new_from_surface(image_surface);
-            gtk_widget_add_tick_callback(GTK_WIDGET(image), (GtkTickCallback) update_YUYV_image, pointer_statusbar, NULL);
-            break;
-        case 'yuv2':case 'UYVY':
-            pthread_create(&init_table_thread, NULL, create_RGBA_from_YUYV, image);
-            width=array->dims[1].length;
-            height=array->dims[2].length;
-            image_buffer = MANY_ALLOCATIONS(width*height * 4, uchar);
-            image_surface=cairo_image_surface_create_for_data(image_buffer, CAIRO_FORMAT_RGB24, width, height, width * 4);
-            image=gtk_image_new_from_surface(image_surface);
-            gtk_widget_add_tick_callback(GTK_WIDGET(image), (GtkTickCallback) update_yuv2_image, pointer_statusbar, NULL);
-            break;
-        default:return NULL;
-    }
-    
-    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 3);
-    
-    scrolled_window=gtk_scrolled_window_new(NULL, NULL);
-    
-    gtk_container_add(GTK_CONTAINER(general_statusbar), framerate_statusbar);
-    gtk_container_add(GTK_CONTAINER(general_statusbar), pointer_statusbar);
-    gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH);
-    
-    init_color_maps();
-    color_map=gray_colors;
-    
-    g_timeout_add(1000, frame_rate, (gpointer)framerate_statusbar);
-    SPRINTF(label_text, "%.4s, %.4s, %dx%d", UINT32_TO_STRING(type_string, array->type), UINT32_TO_STRING(format_string, array->format), width, height);
-    gtk_statusbar_push(GTK_STATUSBAR(general_statusbar), 0, label_text);
-    gtk_widget_set_vexpand(scrolled_window, TRUE);
-    gtk_widget_set_halign(image, GTK_ALIGN_START);
-    gtk_widget_set_valign(image, GTK_ALIGN_START);
-    
-    blgtk_add_tool_button(toolbar, "zoom\n1:1", "zoom-original", G_CALLBACK(zoom_original_cb), NULL);
-    blgtk_add_tool_button(toolbar, "zoom\nx0.5", "zoom-out", G_CALLBACK(zoom_cb), &zoom_in_factor);
-    blgtk_add_tool_button(toolbar, "zoom\nx2", "zoom-in", G_CALLBACK(zoom_cb),  &zoom_out_factor);
-    zoom_button=blgtk_add_toggle_tool_button(toolbar, "zoom\nfit", "zoom-fit-best", NULL, NULL);
-    //    blgtk_add_toggle_tool_button(toolbar, "cursor\nvalues", "system-search", NULL, NULL);/
-    histogram_button=blgtk_add_toggle_tool_button(toolbar, "histogram", "x-office-presentation", G_CALLBACK(histogram_cb), pointer_statusbar);
-    
-    gtk_toggle_tool_button_set_active(zoom_button, TRUE);
-    
-    g_signal_connect(G_OBJECT(scrolled_window), "size-allocate", G_CALLBACK(gtk_image_resize_cb), NULL);
-    
-    event_box=gtk_event_box_new();
-    paned=gtk_paned_new(GTK_ORIENTATION_VERTICAL);
-    //    gtk_paned_set_wide_handle (GTK_PANED(paned), TRUE);
-    gtk_container_add(GTK_CONTAINER(scrolled_window), event_box);
-    gtk_paned_add1(GTK_PANED(paned), scrolled_window);
-    gtk_container_add(GTK_CONTAINER(event_box), image);
-    gtk_container_add(GTK_CONTAINER(vbox), toolbar);
-    gtk_container_add(GTK_CONTAINER(vbox), paned);
-    gtk_container_add(GTK_CONTAINER(vbox), general_statusbar);
-    zoom_original_cb(NULL, NULL);
-    
-    g_signal_connect(window, "window-state-event", G_CALLBACK (toggle_fullscreen), NULL);
-    if (fullscreen_option) gtk_window_fullscreen(GTK_WINDOW(window));
-    
-    // g_signal_connect(G_OBJECT(vbox), "realize", G_CALLBACK(image_realize_cb), NULL);
-    return vbox;
-}
-
-- 
GitLab