├── nix-build-view ├── .gitignore ├── version.hpp ├── VerticalSpacerWidget.hpp ├── ColorManager.hpp ├── VerticalSpacerWidget.cpp ├── ColorManager.cpp ├── TDD-nix-build.hpp ├── FetchWidget.hpp ├── BuildWidgetManager.hpp ├── StatusWidget.hpp ├── BuildWidget.hpp ├── HelpWidget.hpp ├── FetchWidgetManager.hpp ├── WidgetManagerBase.hpp ├── Widget.hpp ├── BuildWidget.cpp ├── TerminalWidget.hpp ├── ColorCodes.h ├── Layout.hpp ├── CMakeLists.txt ├── WindowManager.hpp ├── FetchWidget.cpp ├── nix-build-view.cpp ├── StatusWidget.cpp ├── WidgetManagerBase.cpp ├── BuildWidgetManager.cpp ├── FetchWidgetManager.cpp ├── HelpWidget.cpp ├── TerminalWidget.cpp ├── Layout.cpp ├── WindowManager.cpp ├── TDD-nix-build.cpp ├── AdvancedString.hpp └── TDD-AdvancedStringTest.cpp ├── nix-build-view.jpeg ├── .gitignore ├── default.nix ├── www_curl_easy.pl ├── net_curl_easy.pl ├── design-experiments ├── new_idea.txt ├── cdemo.c ├── preview ├── README ├── demo3.cpp ├── demo.cpp └── demo2.cpp ├── README.md └── 01-curl-transport.pl /nix-build-view/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /nix-build-view.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qknight/nix-build-view/HEAD/nix-build-view.jpeg -------------------------------------------------------------------------------- /nix-build-view/version.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __VERSION_H__ 2 | #define __VERSION_H__ 3 | 4 | #define VERSION "1.0" 5 | 6 | #endif // __VERSION_H__ 7 | -------------------------------------------------------------------------------- /nix-build-view/VerticalSpacerWidget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef VerticalSpacerWidget__HPP 2 | #define VerticalSpacerWidget__HPP 3 | 4 | #include "Widget.hpp" 5 | 6 | class VerticalSpacerWidget : public Widget 7 | { 8 | AdvancedStringContainer render(unsigned int width, unsigned int height); 9 | 10 | int type() const; 11 | 12 | unsigned int rowsWantedByWidget(); 13 | }; 14 | 15 | #endif // VerticalSpacerWidget__HPP 16 | -------------------------------------------------------------------------------- /nix-build-view/ColorManager.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COLORMANAGER_HPP 2 | #define COLORMANAGER_HPP 3 | 4 | #include 5 | #include 6 | 7 | // ncurses abstraction to make transparent use of 8 | // attron(COLOR_PAIR(cm.setColor(color1, color2))) 9 | class ColorManager { 10 | 11 | public: 12 | 13 | int setColor(int bg, int fg); 14 | 15 | private: 16 | 17 | std::map m_map; 18 | 19 | }; 20 | 21 | #endif // COLORMANAGER_HPP 22 | -------------------------------------------------------------------------------- /nix-build-view/VerticalSpacerWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "VerticalSpacerWidget.hpp" 2 | 3 | unsigned int 4 | VerticalSpacerWidget::rowsWantedByWidget() 5 | { 6 | return 0; 7 | } 8 | 9 | int 10 | VerticalSpacerWidget::type() const 11 | { 12 | return WidgetName::VerticalSpacerWidget; 13 | } 14 | 15 | AdvancedStringContainer 16 | VerticalSpacerWidget::render(unsigned int width, unsigned int height) 17 | { 18 | AdvancedStringContainer a; 19 | // a << "hello world"; 20 | return a; 21 | } 22 | 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | demo2 2 | demo 3 | *.swp 4 | .kdev4/ 5 | Launcher/ 6 | cdk/ 7 | demo3 8 | ncurses-moving-resizing-panels.cpp 9 | ncurses/ 10 | ncurses_programs.tar.gz 11 | ncurses_programs/ 12 | nix-build-hack.kdev4 13 | .kdev_include_paths 14 | core 15 | logfile 16 | logfile_ 17 | perl-5.18.2.tar.gz 18 | preview2 19 | *.o 20 | nix-build-view/nix-build-view 21 | nix-build-view/CMakeCache.txt 22 | nix-build-view/CMakeFiles/ 23 | nix-build-view/TDD-AdvancedStringTest 24 | nix-build-view/cmake_install.cmake 25 | nix-build-view/Makefile 26 | -------------------------------------------------------------------------------- /nix-build-view/ColorManager.cpp: -------------------------------------------------------------------------------- 1 | #include "ColorManager.hpp" 2 | #include "ncurses.h" 3 | 4 | int 5 | ColorManager::setColor(int bg, int fg) 6 | { 7 | std::string key = std::to_string(bg) + std::to_string(fg); 8 | 9 | std::map::iterator pos = m_map.find(key); 10 | 11 | if (pos == m_map.end()) { 12 | m_map.insert(std::make_pair(key, m_map.size())); 13 | init_pair(m_map.size(), bg, fg); 14 | return m_map.size(); 15 | } else { 16 | return pos->second+1; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /nix-build-view/TDD-nix-build.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TDDnixbuild 2 | #define TDDnixbuild 3 | 4 | #include 5 | #include 6 | 7 | /* 8 | * emulate nix-build to make usability better without having to integrate 9 | * nix-build-view into nix-build first 10 | * this helps to make performance tests as well... 11 | */ 12 | class NixBuild { 13 | 14 | public: 15 | 16 | NixBuild(); 17 | void tick(); 18 | 19 | private: 20 | 21 | std::vector m; 22 | long millis_old = 0; 23 | std::vector m_fetch; 24 | std::vector m_build; 25 | 26 | }; 27 | 28 | #endif // TDDnixbuild 29 | -------------------------------------------------------------------------------- /nix-build-view/FetchWidget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FETCHWIDGET__HPP 2 | #define FETCHWIDGET__HPP 3 | 4 | #include "WindowManager.hpp" 5 | #include "Widget.hpp" 6 | 7 | class FetchWidget : public Widget { 8 | 9 | friend class FetchWidgetManager; 10 | 11 | public: 12 | 13 | FetchWidget(std::string url, float percent, int bits_per_sec); 14 | 15 | AdvancedStringContainer render(unsigned int width, unsigned int height); 16 | 17 | int type() const; 18 | 19 | float getProgress() const; 20 | 21 | private: 22 | 23 | float m_progress; 24 | 25 | std::string m_name; 26 | 27 | int m_bits_per_sec; 28 | 29 | }; 30 | 31 | #endif // FETCHWIDGET__HPP 32 | -------------------------------------------------------------------------------- /nix-build-view/BuildWidgetManager.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BUILDWIDGETMANAGER_H 2 | #define BUILDWIDGETMANAGER_H 3 | 4 | #include "WidgetManagerBase.hpp" 5 | 6 | class BuildWidgetManager : public WidgetManagerBase { 7 | 8 | public: 9 | 10 | int type() const; 11 | 12 | static BuildWidgetManager* Instance(); 13 | 14 | void addBuild(std::string UUID, std::vector phases); 15 | 16 | void removeBuild(std::string UUID); 17 | 18 | int getPhase(std::string UUID); 19 | 20 | void setPhase(std::string UUID, int phase); 21 | 22 | private: 23 | 24 | BuildWidgetManager(); 25 | 26 | void sort(); 27 | 28 | }; 29 | 30 | #endif // BUILDWIDGETMANAGER_H 31 | -------------------------------------------------------------------------------- /nix-build-view/StatusWidget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef STATUSWIDGET__HPP 2 | #define STATUSWIDGET__HPP 3 | 4 | #include "Widget.hpp" 5 | 6 | class StatusWidget : public Widget { 7 | 8 | AdvancedStringContainer render(unsigned int width, unsigned int height); 9 | 10 | public: 11 | 12 | void setFocus(int arg1); 13 | 14 | int type() const; 15 | 16 | static StatusWidget* Instance(); 17 | 18 | void setBuilds(unsigned int builds); 19 | 20 | void setFetches(unsigned int fetches); 21 | 22 | private: 23 | 24 | StatusWidget(); 25 | 26 | unsigned int m_focus = 1; 27 | unsigned int m_builds = 0; 28 | unsigned int m_fetches = 0; 29 | 30 | }; 31 | 32 | #endif // STATUSWIDGET__HPP 33 | -------------------------------------------------------------------------------- /nix-build-view/BuildWidget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BUILDWIDGET__HPP 2 | #define BUILDWIDGET__HPP 3 | 4 | #include "Widget.hpp" 5 | #include 6 | 7 | class BuildWidget : public Widget { 8 | 9 | friend class BuildWidgetManager; 10 | 11 | public: 12 | 13 | BuildWidget(std::string name, std::vector phases); 14 | 15 | AdvancedStringContainer 16 | render(unsigned int width, unsigned int height); 17 | 18 | int type() const; 19 | 20 | int getPhase() const { 21 | return m_currentPhase; 22 | } 23 | 24 | protected: 25 | 26 | std::string m_name; 27 | std::vector m_phases; 28 | int m_currentPhase; 29 | 30 | }; 31 | 32 | #endif // BUILDWIDGET__HPP 33 | -------------------------------------------------------------------------------- /nix-build-view/HelpWidget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HELPWIDGET__HPP 2 | #define HELPWIDGET__HPP 3 | 4 | #include "WindowManager.hpp" 5 | #include "Widget.hpp" 6 | 7 | class HelpWidget : public Widget { 8 | 9 | AdvancedStringContainer render(unsigned int width, unsigned int height); 10 | 11 | int type() const; 12 | 13 | private: 14 | 15 | std::string p(std::string s); 16 | 17 | unsigned int width() { 18 | return m_width; 19 | } 20 | 21 | unsigned int height() { 22 | return m_height; 23 | } 24 | 25 | unsigned int m_width = 0; 26 | unsigned int m_height = 0; 27 | 28 | std::vector m_terminal; 29 | 30 | }; 31 | 32 | #endif //HELPWIDGET__HPP 33 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | with import {}; 2 | 3 | stdenv.mkDerivation rec { 4 | name = "nix-build-view"; 5 | 6 | src = fetchgit { 7 | url = "https://github.com/qknight/${name}"; 8 | rev = "311110e59e185eb1015dcfd34b30f31a862503bc"; 9 | sha256 = "10wqq9jh1vc83grnmi1ds130i5w8665zm1fg84bncbik03farnch"; 10 | }; 11 | 12 | buildInputs = [ ncurses cmake ]; 13 | 14 | configurePhase =""; 15 | buildPhase = '' 16 | mkdir -p ${name}/build 17 | cd ${name}/build 18 | cmake .. && make 19 | ''; 20 | 21 | installPhase = "mkdir -p $out/bin && mv nix-build-view $out/bin/."; 22 | 23 | meta = { 24 | description = "A monitor for nix-build"; 25 | homepage = "http://blog.lastlog.de/posts/nix-build-view_using_ncurses/"; 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /nix-build-view/FetchWidgetManager.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FETCHWIDGETMANAGER_H 2 | #define FETCHWIDGETMANAGER_H 3 | 4 | #include "WidgetManagerBase.hpp" 5 | 6 | class FetchWidgetManager : public WidgetManagerBase { 7 | 8 | public: 9 | 10 | int type() const; 11 | 12 | static FetchWidgetManager* Instance(); 13 | 14 | void addFetch(std::string UUID, float progress, int bps); 15 | 16 | void removeFetch(std::string UUID); 17 | 18 | float getProgress(std::string UUID) const; 19 | 20 | void setProgress(std::string UUID, float progress); 21 | 22 | int getBPS(std::string UUID); 23 | 24 | void setBPS(std::string UUID, int bits_per_sec); 25 | 26 | private: 27 | 28 | FetchWidgetManager(); 29 | 30 | void sort(); 31 | 32 | }; 33 | 34 | #endif // FETCHWIDGETMANAGER_H 35 | -------------------------------------------------------------------------------- /nix-build-view/WidgetManagerBase.hpp: -------------------------------------------------------------------------------- 1 | #ifndef WIDGETMANAGERBASE_H 2 | #define WIDGETMANAGERBASE_H 3 | 4 | #include "Widget.hpp" 5 | 6 | class WidgetManagerBase : public Widget { 7 | 8 | friend class FetchWidgetManager; 9 | friend class BuildWidgetManager; 10 | 11 | public: 12 | 13 | virtual int type() const = 0; 14 | 15 | AdvancedStringContainer render(unsigned int width, unsigned int height); 16 | 17 | unsigned int rowsWantedByWidget(); 18 | 19 | private: 20 | 21 | void add(Widget* w); 22 | 23 | std::vector m_widgets; 24 | 25 | int m_line = 0; 26 | 27 | void keyboardInputHandler(int ch); 28 | 29 | unsigned int width() { 30 | return m_width; 31 | } 32 | 33 | unsigned int height() { 34 | return m_height; 35 | } 36 | 37 | unsigned int m_width = 0; 38 | unsigned int m_height = 0; 39 | virtual void sort() = 0; 40 | }; 41 | 42 | #endif // WIDGETMANAGERBASE_H 43 | -------------------------------------------------------------------------------- /nix-build-view/Widget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef WIDGET__HPP 2 | #define WIDGET__HPP 3 | 4 | #include "WindowManager.hpp" 5 | #include "AdvancedString.hpp" 6 | 7 | namespace WidgetName { 8 | 9 | enum { 10 | HelpWidget, 11 | TerminalWidget, 12 | FetchWidget, 13 | BuildWidget, 14 | StatusWidget, 15 | VerticalSpacerWidget, 16 | FetchWidgetManager, 17 | BuildWidgetManager, 18 | }; 19 | 20 | }; 21 | 22 | class Widget { 23 | 24 | public: 25 | 26 | virtual AdvancedStringContainer render(unsigned int width, 27 | unsigned int height) = 0; 28 | 29 | void update() { 30 | WindowManager::Instance()->update(this); 31 | } 32 | 33 | virtual int type() const = 0; 34 | virtual void keyboardInputHandler(int ch) {}; 35 | 36 | // for nested widget rendering 37 | virtual unsigned int rowsWantedByWidget() { 38 | return 1; 39 | } 40 | 41 | }; 42 | 43 | #endif // WIDGET__HPP 44 | -------------------------------------------------------------------------------- /nix-build-view/BuildWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "BuildWidget.hpp" 2 | #include 3 | 4 | BuildWidget::BuildWidget(std::string name, 5 | std::vector< std::string > phases) 6 | { 7 | m_name = name; 8 | m_phases = phases; 9 | m_currentPhase = 0; 10 | } 11 | 12 | int 13 | BuildWidget::type() const 14 | { 15 | return WidgetName::BuildWidget; 16 | } 17 | 18 | AdvancedStringContainer 19 | BuildWidget::render(unsigned int width, 20 | unsigned int height) 21 | { 22 | AdvancedStringContainer s; 23 | //FIXME if width is very small, kick the m_name beginning from the left 24 | 25 | if (m_currentPhase < m_phases.size()) { 26 | int i = width - m_name.size() - m_phases[m_currentPhase].size() - 2; 27 | 28 | if (i < 0) { 29 | i = 0; 30 | } 31 | s << AdvancedString(m_name, COLOR_MAGENTA) 32 | << " " 33 | << std::string(i, '.') 34 | << " " 35 | << AdvancedString(m_phases[m_currentPhase], COLOR_YELLOW); 36 | } 37 | 38 | return s; 39 | } 40 | -------------------------------------------------------------------------------- /nix-build-view/TerminalWidget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TERMINALWIDGET__HPP 2 | #define TERMINALWIDGET__HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "Widget.hpp" 11 | 12 | class TerminalWidget : public Widget 13 | { 14 | 15 | public: 16 | 17 | AdvancedStringContainer render(unsigned int width, unsigned int height); 18 | 19 | void append(AdvancedStringContainer line); 20 | 21 | int type() const; 22 | 23 | void keyboardInputHandler(int ch); 24 | 25 | AdvancedStringContainer log() { 26 | return m_logfile; 27 | } 28 | 29 | static TerminalWidget* Instance(); 30 | 31 | private: 32 | 33 | AdvancedStringContainer m_logfile; 34 | unsigned int m_line = 0; 35 | std::vector m_terminal; 36 | 37 | unsigned int width() { 38 | return m_width; 39 | } 40 | 41 | unsigned int height() { 42 | return m_height; 43 | } 44 | 45 | unsigned int m_width = 0; 46 | unsigned int m_height = 0; 47 | 48 | }; 49 | 50 | #endif // TERMINALWIDGET__HPP 51 | -------------------------------------------------------------------------------- /nix-build-view/ColorCodes.h: -------------------------------------------------------------------------------- 1 | #ifndef __COLORCODES_H__ 2 | #define __COLORCODES_H__ 3 | 4 | #define RESET "\033[0m" 5 | #define BLACK "\033[30m" /* Black */ 6 | #define RED "\033[31m" /* Red */ 7 | #define GREEN "\033[32m" /* Green */ 8 | #define YELLOW "\033[33m" /* Yellow */ 9 | #define BLUE "\033[34m" /* Blue */ 10 | #define MAGENTA "\033[35m" /* Magenta */ 11 | #define CYAN "\033[36m" /* Cyan */ 12 | #define WHITE "\033[37m" /* White */ 13 | #define BOLDBLACK "\033[1m\033[30m" /* Bold Black */ 14 | #define BOLDRED "\033[1m\033[31m" /* Bold Red */ 15 | #define BOLDGREEN "\033[1m\033[32m" /* Bold Green */ 16 | #define BOLDYELLOW "\033[1m\033[33m" /* Bold Yellow */ 17 | #define BOLDBLUE "\033[1m\033[34m" /* Bold Blue */ 18 | #define BOLDMAGENTA "\033[1m\033[35m" /* Bold Magenta */ 19 | #define BOLDCYAN "\033[1m\033[36m" /* Bold Cyan */ 20 | #define BOLDWHITE "\033[1m\033[37m" /* Bold White */ 21 | 22 | #endif // __COLORCODES_H__ 23 | -------------------------------------------------------------------------------- /www_curl_easy.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use strict; 3 | use warnings; 4 | use WWW::Curl::Easy; 5 | 6 | $|=1; 7 | 8 | my $curl = WWW::Curl::Easy->new; 9 | 10 | $curl->setopt(CURLOPT_HEADER,1); 11 | $curl->setopt(CURLOPT_URL, 'http://www.cpan.org/src/5.0/perl-5.18.2.tar.gz'); 12 | $curl->setopt(CURLOPT_NOPROGRESS,0); 13 | $curl->setopt(CURLOPT_PROGRESSFUNCTION, sub { my ( $easy, $dltotal, $dlnow, $ultotal, $ulnow, $uservar ) = @_; print join(' ', @_[1..4]), "\n"; return 0; } ); 14 | 15 | # A filehandle, reference to a scalar or reference to a typeglob can be used here. 16 | open my $fh, '>', 'perl-5.18.2.tar.gz' or die "$!\n"; 17 | $curl->setopt(CURLOPT_WRITEDATA,$fh); 18 | 19 | # Starts the actual request 20 | my $retcode = $curl->perform; 21 | 22 | # Looking at the results... 23 | if ($retcode == 0) { 24 | print("Transfer went ok\n"); 25 | my $response_code = $curl->getinfo(CURLINFO_HTTP_CODE); 26 | # judge result and next action based on $response_code 27 | } else { 28 | # Error code, type of error, error message 29 | print("An error happened: $retcode ".$curl->strerror($retcode)." ".$curl->errbuf."\n"); 30 | } 31 | -------------------------------------------------------------------------------- /net_curl_easy.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use strict; 3 | use warnings; 4 | use Net::Curl::Easy qw[:constants]; 5 | 6 | $|=1; 7 | 8 | my $curl = Net::Curl::Easy->new; 9 | 10 | $curl->setopt(CURLOPT_HEADER,1); 11 | $curl->setopt(CURLOPT_URL, 'http://www.cpan.org/src/5.0/perl-5.18.2.tar.gz'); 12 | $curl->setopt(CURLOPT_NOPROGRESS,0); 13 | $curl->setopt(CURLOPT_PROGRESSFUNCTION, sub { my ( $easy, $dltotal, $dlnow, $ultotal, $ulnow, $uservar ) = @_; print join(' ', @_[1..4]), "\n"; return 0; } ); 14 | 15 | # A filehandle, reference to a scalar or reference to a typeglob can be used here. 16 | open my $fh, '>', 'perl-5.18.2.tar.gz' or die "$!\n"; 17 | $curl->setopt(CURLOPT_WRITEDATA,$fh); 18 | 19 | # Starts the actual request 20 | my $retcode = $curl->perform; 21 | 22 | # Looking at the results... 23 | if ($retcode == 0) { 24 | print("Transfer went ok\n"); 25 | my $response_code = $curl->getinfo(CURLINFO_HTTP_CODE); 26 | # judge result and next action based on $response_code 27 | } else { 28 | # Error code, type of error, error message 29 | print("An error happened: $retcode ".$curl->strerror($retcode)." ".$curl->errbuf."\n"); 30 | } 31 | -------------------------------------------------------------------------------- /nix-build-view/Layout.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LAYOUT__HPP 2 | #define LAYOUT__HPP 3 | 4 | #include "Widget.hpp" 5 | 6 | class FixedWidget { 7 | 8 | public: 9 | 10 | Widget* widget; 11 | unsigned int width = 0; 12 | unsigned int height = 0; 13 | 14 | }; 15 | 16 | class RasterizedLayout { 17 | 18 | public: 19 | 20 | std::vector m_fixedWidgets; 21 | }; 22 | 23 | class LayoutItem { 24 | 25 | public: 26 | 27 | LayoutItem(Widget* w, unsigned int hH) { 28 | widget = w; 29 | heightHint = hH; 30 | } 31 | 32 | Widget* widget; 33 | unsigned int heightHint=1; 34 | }; 35 | 36 | 37 | class Layout { 38 | 39 | public: 40 | 41 | void addWidget(Widget* w, unsigned int sizeHint = 1); 42 | RasterizedLayout rasterize(int width, int height); 43 | 44 | // unsigned int width() { 45 | // return m_width; 46 | // } 47 | // unsigned int height() { 48 | // return m_height; 49 | // } 50 | 51 | std::vector m_layoutItems; 52 | 53 | // private: 54 | // unsigned int m_width = 0; 55 | // unsigned int m_height = 1; 56 | 57 | }; 58 | 59 | #endif //LAYOUT__HPP 60 | -------------------------------------------------------------------------------- /design-experiments/new_idea.txt: -------------------------------------------------------------------------------- 1 | \ machine 1 | machine 2 | machine 3 | ... / 2 | `-----------| |-----------^----------------' 3 | .-----------' `-----------------------------. 4 | | .-----------------------. .-----------------------. | 5 | | | build 1 | | build 3 | | 6 | | |-----------------------| |-----------------------| | 7 | | | line 1 | | line 1 | | 8 | | | line 2 | | line 2 | | 9 | | | line 3 | | line 3 | | 10 | | | line 4 | | line 4 | | 11 | | | line 5 | | line 5 | | 12 | | `-----------------------' `-----------------------' | 13 | | .-----------------------. .-----------------------. | 14 | | | build 2 | | build 4 | | 15 | | |-----------------------| |-----------------------| | 16 | | | line 1 | | line 1 | | 17 | | | line 2 | | line 2 | | 18 | | | line 3 | | line 3 | | 19 | | | line 4 | | line 4 | | 20 | | | line 5 | | line 5 | | 21 | | `-----------------------' `-----------------------' | 22 | `-----------------------------------------------------' -------------------------------------------------------------------------------- /nix-build-view/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | FIND_PACKAGE(Curses) 3 | 4 | # Find includes in corresponding build directories 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | 7 | SET(nix-build-view_SOURCES ${nix-build-view_SOURCES} 8 | nix-build-view.cpp 9 | Layout.cpp 10 | WindowManager.cpp 11 | TerminalWidget.cpp 12 | StatusWidget.cpp 13 | FetchWidget.cpp 14 | BuildWidget.cpp 15 | ColorManager.cpp 16 | HelpWidget.cpp 17 | VerticalSpacerWidget.cpp 18 | BuildWidgetManager.cpp 19 | FetchWidgetManager.cpp 20 | WidgetManagerBase.cpp 21 | TDD-nix-build.cpp 22 | ) 23 | 24 | SET(TDD-AdvancedString_SOURCES ${TDD-AdvancedString_SOURCES} 25 | TDD-AdvancedStringTest.cpp 26 | ) 27 | 28 | #-std=c++0x or -std=gnu++0x 29 | ADD_DEFINITIONS("-std=c++11") 30 | #ADD_DEFINITIONS("-O0") 31 | 32 | ADD_DEFINITIONS("-Wall") 33 | 34 | ADD_EXECUTABLE ( TDD-AdvancedStringTest ${TDD-AdvancedString_SOURCES}) 35 | 36 | if (CURSES_FOUND) 37 | ADD_EXECUTABLE ( nix-build-view ${nix-build-view_SOURCES}) 38 | 39 | TARGET_LINK_LIBRARIES( nix-build-view ${CURSES_LIBRARIES}) 40 | 41 | INCLUDE_DIRECTORIES( 42 | ${CURSES_INCLUDE_DIR} 43 | ${CMAKE_CURRENT_SOURCE_DIR} 44 | ) 45 | 46 | TARGET_LINK_LIBRARIES ( nix-build-view ncurses) 47 | INSTALL ( TARGETS nix-build-view DESTINATION bin ) 48 | 49 | else (CURSES_FOUND) 50 | MESSAGE(FATAL_ERROR "ncurses library not found, can't build nix-build-view then!") 51 | endif (CURSES_FOUND) 52 | 53 | 54 | -------------------------------------------------------------------------------- /nix-build-view/WindowManager.hpp: -------------------------------------------------------------------------------- 1 | #ifndef WINDOW_MANAGER_HPP 2 | #define WINDOW_MANAGER_HPP 3 | 4 | #include 5 | #include "version.hpp" 6 | 7 | #include "ColorManager.hpp" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | class Widget; 15 | class Layout; 16 | class AdvancedString; 17 | class TerminalWidget; 18 | class StatusWidget; 19 | class HelpWidget; 20 | class VerticalSpacerWidget; 21 | 22 | class WindowManager { 23 | 24 | public: 25 | 26 | void update(Widget* w = NULL); 27 | 28 | void setLayout(int layout); 29 | 30 | void addLayout(Layout* l); 31 | 32 | void resize(int width, int height); 33 | 34 | int width() { 35 | return m_width; 36 | }; 37 | 38 | int height() { 39 | return m_height; 40 | }; 41 | 42 | static WindowManager* Instance(); 43 | 44 | void keyboardInputHandler(int ch); 45 | 46 | std::string version(); 47 | 48 | int EventLoop(); 49 | 50 | private: 51 | 52 | WindowManager(WINDOW *win); 53 | 54 | void setKeyboardInputHandler(Widget* w); 55 | 56 | WINDOW* m_win; 57 | 58 | int m_width; 59 | 60 | int m_height; 61 | 62 | ColorManager cm; 63 | 64 | unsigned int m_selectedLayout = 0; 65 | 66 | StatusWidget* statusWidget; 67 | 68 | HelpWidget* helpWidget; 69 | 70 | VerticalSpacerWidget* verticalSpacer; 71 | 72 | std::vector m_layouts; 73 | 74 | Widget* m_focusWidget = NULL; 75 | 76 | int main_loop = 1; 77 | 78 | }; 79 | 80 | #endif // WINDOW_MANAGER_HPP 81 | -------------------------------------------------------------------------------- /nix-build-view/FetchWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "FetchWidget.hpp" 2 | #include "AdvancedString.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | FetchWidget::FetchWidget(std::string url, float progress, int bits_per_sec) 11 | { 12 | m_name=url; 13 | m_progress = progress; 14 | m_bits_per_sec = bits_per_sec; 15 | }; 16 | 17 | int 18 | FetchWidget::type() const 19 | { 20 | return WidgetName::FetchWidget; 21 | } 22 | 23 | AdvancedStringContainer 24 | FetchWidget::render(unsigned int width, unsigned int height) 25 | { 26 | AdvancedStringContainer url_progress; 27 | 28 | //FIXME if width is very small, kick the m_name beginning from the left 29 | //FIXME compute kib/Mib/Gib labels from input 30 | stringstream s1; 31 | s1 << setw(3) << right << (int)(m_progress * 100) << "% " 32 | << setw(4) << setprecision(3) << 26.3333 << "Mib " 33 | << setw(4) << setprecision(4) << 123.31333 << "kib/s"; 34 | 35 | AdvancedStringContainer s2; 36 | 37 | // dynamic spacer 38 | int i = width - m_name.size() - s1.str().size() - 1; 39 | if (i < 0) { 40 | i = 0; 41 | } 42 | 43 | std::stringstream t; 44 | t << m_name << " " << std::string(i+1, '.'); 45 | 46 | int size = t.str().size()-2; 47 | float end = m_progress * size; 48 | 49 | url_progress << AdvancedString(t.str().substr(0, (int) end), COLOR_GREEN) 50 | << t.str().substr((int) end, size - (int) end); 51 | 52 | s2 << url_progress << " " << AdvancedString(s1.str(), COLOR_YELLOW); 53 | 54 | return s2; 55 | } 56 | 57 | float 58 | FetchWidget::getProgress() const 59 | { 60 | return m_progress; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # motivation 2 | 3 | This is an interactive showcase how nix-build could be extended for better 4 | usability using a GUI based on ncurses. 5 | 6 | `nix-build` uses perl scripts to download NAR files, these 3 examples implement 7 | a callback which can be used to query the status of such downloads: 8 | 9 | * `01-curl-transport.pl` 10 | * `net_curl_easy.pl` 11 | * `www_curl_easy.pl` 12 | 13 | In nix-build-view I've written a ncurses based view with advanced color string 14 | handling. 15 | 16 | ![Nix build view](nix-build-view.jpeg) 17 | 18 | # Outstanding concepts 19 | 20 | I wanted to have colored widgets thus I had to write my own `WindowManager` and 21 | I added my own `AdvancedString` implementation. Please feel free to copy them. 22 | 23 | Note: `AdvancedString` could need some profiling but it is fast enough for this 24 | purpose. 25 | 26 | # Licensing 27 | 28 | I'm not sure which license to pick. two options: 29 | 30 | * If I merge this source into nix-build i will pick BSD (this is what nix-build 31 | is licensed to) 32 | * If it is decided that nix-build-view should be 'just a view' and thus be its 33 | own process GPLv3 would be a good pick 34 | 35 | However, I'm open to discussion! 36 | 37 | # How to get nix-build-view compiled & running 38 | 39 | Note: This repository is called nix-build-view _but_ also contains a directory 40 | called nix-build-view! 41 | 42 | Using stdenv (GCC): 43 | 44 | ``` 45 | cd nix-build-view 46 | nix-shell -p ncurses cmake 47 | mkdir build; cd build 48 | cmake .. 49 | make 50 | ./nix-build-view 51 | ``` 52 | 53 | In case you want to use clang (LLVM): 54 | 55 | ``` 56 | cd nix-build-view 57 | nix-shell -p ncurses cmake clang 58 | mkdir build; cd build 59 | export CC=clang 60 | export CXX=clang++ 61 | cmake .. 62 | make 63 | ./nix-build-view 64 | ``` 65 | -------------------------------------------------------------------------------- /nix-build-view/nix-build-view.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "WindowManager.hpp" 5 | #include "AdvancedString.hpp" 6 | #include "TerminalWidget.hpp" 7 | #include "ColorCodes.h" 8 | #include "TDD-nix-build.hpp" 9 | 10 | #define TIME_OUT 1000 11 | 12 | void 13 | keyboard_input_handler() 14 | { 15 | int ch; 16 | timeout(TIME_OUT); 17 | ch = getch(); /* Waits for TIME_OUT milliseconds */ 18 | 19 | if (ch == ERR) { 20 | return; 21 | } 22 | 23 | // all other inputs are delegated to the windowmanager 24 | WindowManager::Instance()->keyboardInputHandler(ch); 25 | } 26 | 27 | int 28 | main(int argc, char *argv[]) 29 | { 30 | initscr(); /* Start curses mode */ 31 | start_color(); 32 | cbreak(); /* Line buffering disabled. Pass on every thing */ 33 | keypad(stdscr, TRUE); 34 | curs_set(FALSE); 35 | 36 | noecho(); 37 | 38 | WindowManager::Instance()->update(); 39 | 40 | AdvancedStringContainer s; 41 | s << AdvancedString("nix-build-view ", COLOR_CYAN, WA_BOLD) 42 | << AdvancedString(WindowManager::Instance()->version(), COLOR_CYAN) 43 | << AdvancedString(" (c) 2014++ Joachim Schiele\n", COLOR_CYAN, WA_BOLD); 44 | s << AdvancedString("Released under the GNU GPL v3\n", COLOR_CYAN, WA_BOLD); 45 | s << AdvancedString("\n"); 46 | 47 | TerminalWidget::Instance()->append(s); 48 | 49 | NixBuild* nixBuild = new NixBuild(); 50 | 51 | while (WindowManager::Instance()->EventLoop()) { 52 | nixBuild->tick(); // simulates nix-build events 53 | keyboard_input_handler(); 54 | } 55 | 56 | endwin(); /* End curses mode */ 57 | 58 | AdvancedStringContainer log = TerminalWidget::Instance()->log(); 59 | for (unsigned int i = 0; i < log.size(); ++i) { 60 | std::cout << log[i].terminalString(); 61 | } 62 | 63 | return 0; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /nix-build-view/StatusWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "StatusWidget.hpp" 2 | #include "AdvancedString.hpp" 3 | #include 4 | 5 | int 6 | StatusWidget::type() const 7 | { 8 | return WidgetName::StatusWidget; 9 | } 10 | 11 | AdvancedStringContainer 12 | StatusWidget::render(unsigned int width, unsigned int height) 13 | { 14 | AdvancedStringContainer s1; 15 | 16 | AdvancedStringContainer s2; 17 | std::vector el; 18 | 19 | el.push_back(" h help "); 20 | el.push_back(" 1 combined "); 21 | el.push_back(" 2 log "); 22 | el.push_back(" 3 fetching "); 23 | el.push_back(" 4 building "); 24 | 25 | s2 << "["; 26 | for (unsigned int i = 0; i < el.size(); ++i) { 27 | if (i == m_focus) { 28 | s2 << AdvancedString(el[i], COLOR_WHITE, 0, COLOR_BLUE); 29 | } else { 30 | s2 << el[i]; 31 | } 32 | 33 | if (i != el.size() - 1) { 34 | s2 << "|"; 35 | } 36 | } 37 | s2 << "]"; 38 | 39 | if (m_fetches) { 40 | s2 << " " << AdvancedString(std::to_string(m_fetches), COLOR_GREEN); 41 | 42 | auto str = (m_fetches == 1 ? " fetch" : " fetches"); 43 | s2 << AdvancedString(str, COLOR_GREEN); 44 | } 45 | 46 | if (m_builds) { 47 | s2 << " " << AdvancedString(std::to_string(m_builds), COLOR_MAGENTA); 48 | auto str = (m_builds == 1 ? " build" : " builds"); 49 | s2 << AdvancedString(str, COLOR_MAGENTA); 50 | } 51 | 52 | int i = width - s1.size(); 53 | if (i < 0) { 54 | i = 0; 55 | } 56 | 57 | return s2; 58 | } 59 | 60 | void 61 | StatusWidget::setFocus(int focus) 62 | { 63 | m_focus = focus; 64 | update(); 65 | } 66 | 67 | StatusWidget* 68 | StatusWidget::Instance() 69 | { 70 | static StatusWidget* _instance = new StatusWidget; 71 | return _instance; 72 | } 73 | 74 | StatusWidget::StatusWidget() 75 | { 76 | } 77 | 78 | void 79 | StatusWidget::setBuilds(unsigned int builds) 80 | { 81 | m_builds = builds; 82 | update(); 83 | } 84 | 85 | void 86 | StatusWidget::setFetches(unsigned int fetches) 87 | { 88 | m_fetches = fetches; 89 | update(); 90 | } 91 | 92 | -------------------------------------------------------------------------------- /design-experiments/cdemo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define CURSOR_TO_START_OF_LINE "\r" 5 | #define CURSOR_UP_ONE_LINE "\e[A" 6 | #define CURSOR_CLEAR_TO_EOL "\e[K" 7 | #define CURSOR_CLEAN_ALL_AFTERWARDS "\e[J" 8 | 9 | #define RESET "\033[0m" 10 | #define BLACK "\033[30m" /* Black */ 11 | #define RED "\033[31m" /* Red */ 12 | #define GREEN "\033[32m" /* Green */ 13 | #define YELLOW "\033[33m" /* Yellow */ 14 | #define BLUE "\033[34m" /* Blue */ 15 | #define MAGENTA "\033[35m" /* Magenta */ 16 | #define CYAN "\033[36m" /* Cyan */ 17 | #define WHITE "\033[37m" /* White */ 18 | #define BOLDBLACK "\033[1m\033[30m" /* Bold Black */ 19 | #define BOLDRED "\033[1m\033[31m" /* Bold Red */ 20 | #define BOLDGREEN "\033[1m\033[32m" /* Bold Green */ 21 | #define BOLDYELLOW "\033[1m\033[33m" /* Bold Yellow */ 22 | #define BOLDBLUE "\033[1m\033[34m" /* Bold Blue */ 23 | #define BOLDMAGENTA "\033[1m\033[35m" /* Bold Magenta */ 24 | #define BOLDCYAN "\033[1m\033[36m" /* Bold Cyan */ 25 | #define BOLDWHITE "\033[1m\033[37m" /* Bold White */ 26 | 27 | int update(int foo) { 28 | int i=0; 29 | if (foo != 0) { 30 | for (i=0; i < 8; i++) { 31 | //printf(CURSOR_TO_START_OF_LINE); 32 | printf(CURSOR_UP_ONE_LINE); 33 | //printf(CURSOR_CLEAR_TO_EOL); 34 | } 35 | printf(CURSOR_CLEAN_ALL_AFTERWARDS); 36 | } 37 | printf("some log line %i\n", foo); 38 | printf("----------------------------------------------\n"); 39 | 40 | if (foo != 5) 41 | printf("building: \n" 42 | " /nix/store/ylcpwyczz887grq8lzdz8hn81q7yrn38-gzip-1.6\n" 43 | " /nix/store/j298bijkgdzzv6wlzdidldx297ch5rq2-nix-1.7pre3327_0e2ca26\n" 44 | "fetching: \n" 45 | " http://cache.nixos.org/nar/0s57kyi85g7lb9irja2cslmh5vc23i4q35dv8pi4gh19k0jc7nf3.nar.xz  50 - %u 1.82 M/s\n" 46 | " http://cache.nixos.org/nar/0s57kyi85g7lb9irja2cslmh5vc23i4q35dv8pi4gh19k0jc7nf3.nar.xz  70 - %u 10.25 M/s\n" 47 | " http://cache.nixos.org/nar/0s55vc23i4q35dv8pi4gh19k0jc7nf3.nar.xz.....................  49 - %u 0.25 M/s\n", foo, foo , foo); 48 | } 49 | 50 | int main() { 51 | int foo = 0; 52 | printf("---------------------------------------------\n"); 53 | while(1) { 54 | update(foo); 55 | if(foo==5) 56 | exit(0); 57 | foo+=1; 58 | sleep(1); 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /nix-build-view/WidgetManagerBase.cpp: -------------------------------------------------------------------------------- 1 | #include "WidgetManagerBase.hpp" 2 | 3 | AdvancedStringContainer 4 | WidgetManagerBase::render(unsigned int width, unsigned int height) 5 | { 6 | m_width = width; 7 | m_height = height; 8 | 9 | sort(); 10 | 11 | // if elements vanish you don't have to scroll up manually 12 | if(m_line > m_widgets.size() - height) { 13 | m_line = m_widgets.size() - height; 14 | } 15 | 16 | AdvancedStringContainer sc; 17 | for (unsigned int i = m_line; i < m_line+height; ++i) { 18 | if (i >= m_widgets.size()) { 19 | break; 20 | } 21 | 22 | AdvancedStringContainer a = m_widgets[i]->render(width, 1); 23 | 24 | // limits the stringsize to width (if it was longer) 25 | int left = width * 1; 26 | for (unsigned int x = 0; x < a.size(); ++x) { 27 | if(!left) { 28 | break; 29 | } 30 | 31 | AdvancedString as; 32 | if (left < a[x].size()) { 33 | as = AdvancedString::substr(a[x], 0, left); 34 | } else { 35 | as = a[x]; 36 | } 37 | 38 | sc << as; 39 | left -= as.size(); 40 | } 41 | } 42 | 43 | return sc; 44 | } 45 | 46 | unsigned int 47 | WidgetManagerBase::rowsWantedByWidget() 48 | { 49 | return m_widgets.size(); 50 | } 51 | 52 | void 53 | WidgetManagerBase::keyboardInputHandler(int ch) 54 | { 55 | switch(ch) { 56 | case(KEY_HOME): 57 | m_line = 0; 58 | update(); 59 | break; 60 | 61 | case(KEY_END): 62 | m_line = m_widgets.size()-height(); 63 | update(); 64 | break; 65 | 66 | case(KEY_UP): 67 | m_line -= 1; 68 | if (m_line < 0) { 69 | m_line = 0; 70 | } 71 | update(); 72 | break; 73 | 74 | case(KEY_DOWN): 75 | if (m_line >= 0) { 76 | m_line += 1; 77 | } 78 | 79 | if(m_line > m_widgets.size() - height()) { 80 | m_line = m_widgets.size() - height(); 81 | } 82 | update(); 83 | break; 84 | 85 | case(KEY_PPAGE): 86 | m_line -= 15; 87 | if (m_line < 0) { 88 | m_line = 0; 89 | } 90 | update(); 91 | break; 92 | 93 | case(KEY_NPAGE): 94 | if (m_line >= 0) { 95 | m_line += 15; 96 | } 97 | 98 | if (m_line > m_widgets.size() - height()) { 99 | m_line = m_widgets.size() - height(); 100 | } 101 | 102 | update(); 103 | break; 104 | 105 | default: 106 | break; 107 | 108 | } 109 | } 110 | 111 | void 112 | WidgetManagerBase::add(Widget* w) 113 | { 114 | m_widgets.push_back(w); 115 | sort(); 116 | update(); 117 | } 118 | 119 | -------------------------------------------------------------------------------- /nix-build-view/BuildWidgetManager.cpp: -------------------------------------------------------------------------------- 1 | #include "BuildWidgetManager.hpp" 2 | #include "BuildWidget.hpp" 3 | #include "TerminalWidget.hpp" 4 | #include "StatusWidget.hpp" 5 | 6 | #include 7 | 8 | BuildWidgetManager::BuildWidgetManager() 9 | { 10 | } 11 | 12 | int 13 | BuildWidgetManager::type() const 14 | { 15 | return WidgetName::BuildWidgetManager; 16 | } 17 | 18 | BuildWidgetManager* 19 | BuildWidgetManager::Instance() 20 | { 21 | static BuildWidgetManager* _instance = new BuildWidgetManager; 22 | return _instance; 23 | } 24 | 25 | bool 26 | BuildWidgetManagerSort(const Widget* a, const Widget* b) 27 | { 28 | if ((a->type() == WidgetName::BuildWidget) && 29 | (b->type() == WidgetName::BuildWidget)) 30 | { 31 | const BuildWidget* f1 = dynamic_cast(a); 32 | const BuildWidget* f2 = dynamic_cast(b); 33 | 34 | return f1->getPhase() > f2->getPhase(); 35 | } 36 | 37 | return false; 38 | } 39 | 40 | void 41 | BuildWidgetManager::sort() 42 | { 43 | std::stable_sort(m_widgets.begin(), 44 | m_widgets.end(), 45 | BuildWidgetManagerSort); 46 | } 47 | 48 | // it is assumed that the given UUID is already uniq 49 | void 50 | BuildWidgetManager::addBuild(std::string UUID, 51 | std::vector phases) 52 | { 53 | add(new BuildWidget(UUID, phases)); 54 | StatusWidget::Instance()->setBuilds(m_widgets.size()); 55 | } 56 | 57 | void 58 | BuildWidgetManager::removeBuild(std::string UUID) 59 | { 60 | //FIXME todo 61 | StatusWidget::Instance()->setBuilds(m_widgets.size()); 62 | } 63 | 64 | int 65 | BuildWidgetManager::getPhase(std::string UUID) 66 | { 67 | //FIXME refactor this function into a general resolver function 68 | for(unsigned int i = 0; i < m_widgets.size(); ++i) { 69 | BuildWidget* v = dynamic_cast(m_widgets[i]); 70 | 71 | if (v->m_name == UUID) { 72 | return v->m_currentPhase; 73 | } 74 | } 75 | 76 | return -1; 77 | } 78 | 79 | void 80 | BuildWidgetManager::setPhase(std::string UUID, int phase) 81 | { 82 | for(unsigned int i = 0; i < m_widgets.size(); ++i) 83 | { 84 | BuildWidget* v = dynamic_cast(m_widgets[i]); 85 | 86 | if (v->m_name == UUID) { 87 | v->m_currentPhase = phase; 88 | 89 | if (v->m_currentPhase == v->m_phases.size()) { 90 | AdvancedStringContainer v; 91 | v << "Build of " 92 | << AdvancedString(UUID, COLOR_MAGENTA) 93 | << " completed\n"; 94 | 95 | TerminalWidget::Instance()->append(v); 96 | sort(); 97 | m_widgets.erase(m_widgets.begin()); 98 | update(); 99 | 100 | // FIXME when removing elements, move the view upwards since it 101 | // runs out of items currently 102 | StatusWidget::Instance()->setBuilds(m_widgets.size()); 103 | 104 | // FIXME use interl remove fkt 105 | } 106 | 107 | return; 108 | } 109 | } 110 | } 111 | 112 | -------------------------------------------------------------------------------- /nix-build-view/FetchWidgetManager.cpp: -------------------------------------------------------------------------------- 1 | #include "FetchWidgetManager.hpp" 2 | #include "FetchWidget.hpp" 3 | #include "TerminalWidget.hpp" 4 | #include "StatusWidget.hpp" 5 | 6 | #include 7 | #include 8 | 9 | 10 | FetchWidgetManager::FetchWidgetManager() 11 | { 12 | } 13 | 14 | int 15 | FetchWidgetManager::type() const 16 | { 17 | return WidgetName::FetchWidgetManager; 18 | } 19 | 20 | FetchWidgetManager* 21 | FetchWidgetManager::Instance() 22 | { 23 | static FetchWidgetManager* _instance = new FetchWidgetManager; 24 | return _instance; 25 | } 26 | 27 | bool 28 | FetchWidgetManagerSort(const Widget* a, const Widget* b) 29 | { 30 | if ((a->type() == WidgetName::FetchWidget) && 31 | (b->type() == WidgetName::FetchWidget)) 32 | { 33 | const FetchWidget* f1 = dynamic_cast(a); 34 | const FetchWidget* f2 = dynamic_cast(b); 35 | 36 | return f1->getProgress() > f2->getProgress(); 37 | } 38 | 39 | return false; 40 | } 41 | 42 | void 43 | FetchWidgetManager::sort() 44 | { 45 | std::stable_sort(m_widgets.begin(), 46 | m_widgets.end(), 47 | FetchWidgetManagerSort); 48 | } 49 | 50 | // it is assumed that the given UUID is already uniq 51 | void 52 | FetchWidgetManager::addFetch(std::string UUID, float progress, int bps) 53 | { 54 | add(new FetchWidget(UUID, progress, bps)); 55 | StatusWidget::Instance()->setFetches(m_widgets.size()); 56 | } 57 | 58 | void 59 | FetchWidgetManager::removeFetch(std::string UUID) 60 | { 61 | //FIXME todo 62 | StatusWidget::Instance()->setFetches(m_widgets.size()); 63 | } 64 | 65 | int 66 | FetchWidgetManager::getBPS(std::string UUID) 67 | { 68 | //FIXME todo 69 | return 0; 70 | } 71 | 72 | float 73 | FetchWidgetManager::getProgress(std::string UUID) const 74 | { 75 | //FIXME refactor this function since it is used more often in this class 76 | for(unsigned int i = 0; i < m_widgets.size(); ++i) 77 | { 78 | FetchWidget* v = dynamic_cast(m_widgets[i]); 79 | if (v->m_name == UUID) { 80 | return v->m_progress; 81 | } 82 | } 83 | 84 | return 0.0f; 85 | } 86 | 87 | void 88 | FetchWidgetManager::setBPS(std::string UUID, int bits_per_sec) 89 | { 90 | //FIXME todo 91 | } 92 | 93 | void 94 | FetchWidgetManager::setProgress(std::string UUID, float progress) 95 | { 96 | for(unsigned int i = 0; i < m_widgets.size(); ++i) 97 | { 98 | FetchWidget* v = dynamic_cast(m_widgets[i]); 99 | 100 | if (v->m_name == UUID) { 101 | v->m_progress = progress; 102 | 103 | if (progress >= 1.0f) { 104 | AdvancedStringContainer v; 105 | v << "Fetch of " 106 | << AdvancedString(UUID, COLOR_GREEN) 107 | << " completed\n"; 108 | 109 | TerminalWidget::Instance()->append(v); 110 | sort(); 111 | m_widgets.erase(m_widgets.begin()); 112 | update(); 113 | 114 | // FIXME when removing elements, move the view upwards since it 115 | // runs out of items currently 116 | StatusWidget::Instance()->setFetches(m_widgets.size()); 117 | } 118 | 119 | return; 120 | } 121 | } 122 | } 123 | 124 | -------------------------------------------------------------------------------- /nix-build-view/HelpWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "HelpWidget.hpp" 2 | #include "WindowManager.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | int 9 | HelpWidget::type() const 10 | { 11 | return WidgetName::HelpWidget; 12 | } 13 | 14 | std::string 15 | HelpWidget::p(std::string s) 16 | { 17 | std::stringstream ss; 18 | ss << std::setw(10) << std::left << s; 19 | 20 | return ss.str(); 21 | } 22 | 23 | AdvancedStringContainer 24 | HelpWidget::render(unsigned int width, unsigned int height) 25 | { 26 | //copy the last h elements from terminal to the out buffer 27 | AdvancedStringContainer out; 28 | AdvancedStringContainer s; 29 | 30 | s << AdvancedString("nix-build-view ", COLOR_CYAN, WA_BOLD) 31 | << AdvancedString(WindowManager::Instance()->version(), COLOR_CYAN) 32 | << AdvancedString(" (c) 2014++ Joachim Schiele\n", COLOR_CYAN, WA_BOLD) 33 | 34 | << AdvancedString("Released under the GNU GPL v3\n", COLOR_CYAN, WA_BOLD) 35 | 36 | #define __as(txt) AdvancedString((txt), COLOR_CYAN, WA_BOLD) 37 | << "\n" 38 | << "\n" 39 | << " " << "keyboard shortcuts: \n" 40 | << "\n" 41 | << " " << __as(p("q")) 42 | << " - quit the program\n" 43 | 44 | << "\n" 45 | << " " << __as(p("h")) 46 | << " - help\n" 47 | 48 | << " " << __as(p("1")) 49 | << " - combined view with log+fetch+build\n" 50 | 51 | << " " << __as(p("2")) 52 | << " - shows the logfile output of nix-build-view\n" 53 | 54 | << " " << __as(p("3")) 55 | << " - shows the download widget (can be scrolled)\n" 56 | 57 | << " " << __as(p("4")) 58 | << " - shows the build widget (can be scrolled)\n" 59 | 60 | << "\n" 61 | << " " << "using the views: \n" 62 | << "\n" 63 | << " " << __as(p("up")) 64 | << " - scrolls view up\n" 65 | 66 | << " " << __as(p("down")) 67 | << " - scrolls the view down\n" 68 | 69 | << " " << __as(p("page up")) 70 | << " - scrolls view up by several lines\n" 71 | 72 | << " " << __as(p("page down")) 73 | << " - scrolls view down by several lines\n" 74 | 75 | << " " << __as(p("pos1")) 76 | << " - scrolls view to beginning\n" 77 | 78 | << " " << __as(p("end")) 79 | << " - scrolls view to end\n" 80 | #undef __as 81 | 82 | << "\n" 83 | << " " 84 | << "note: if you scroll to the end of the log (2) 'auto-scroll' will be active\n"; 85 | 86 | 87 | //caches the output for better performance 88 | if ((m_width != width) || (m_height != height)) { 89 | m_width = width; 90 | m_height = height; 91 | AdvancedStringContainer::terminal_rasterize(m_terminal, s, width); 92 | } 93 | 94 | std::vector::const_iterator it_b = m_terminal.begin(); 95 | std::vector::const_iterator it_e = m_terminal.end(); 96 | 97 | int m_line = 0; 98 | 99 | if (it_e - height - m_line >= it_b) { 100 | it_b = it_e - height - m_line; 101 | } 102 | 103 | for (unsigned int i = 0; i < height; ++i) { 104 | if (it_b >= it_e) { 105 | break; 106 | } 107 | 108 | AdvancedStringContainer t = *it_b++; 109 | out << t; 110 | } 111 | 112 | return out; 113 | } 114 | 115 | -------------------------------------------------------------------------------- /design-experiments/preview: -------------------------------------------------------------------------------- 1 | building Nix... 2 | these derivations will be built: 3 | /nix/store/4hh3935anmmhl13q8lmdkgdsxzh46gq6-tig-1.1.drv 4 | /nix/store/9brfbp9gk7x3qim52q49rckhw7vw08h2-asciidoc-8.6.8.drv 5 | /nix/store/ckn6dnnb0ayjbdn3avpqvqa5rs35k2w1-tig-1.1.tar.gz.drv 6 | /nix/store/j9bgfp4q6h8gdi4b6idvi1r39b5hagvz-asciidoc-8.6.8.tar.gz.drv 7 | these paths will be fetched (40.1 Mib download, 201.66 Mib unpacked): 8 | /nix/store/0yzz6p08k1sgpdb63c0wx48vx0yc51g6-bzip2-1.0.6 9 | /nix/store/1a08qk5q5vdfv13rwasbf4fqa2s26kx4-attr-2.4.47 10 | /nix/store/3amm865b2qb5s5mwvshvd9kpfq3aj1bc-libssh2-1.4.3 11 | /nix/store/5myfmphlck9gcabr6czlg6792d9zhh4m-perl-DBI-1.630 12 | /nix/store/j298bijkgdzzv6wlzdidldx297ch5rq2-nix-1.7pre3327_0e2ca 13 | /nix/store/mad928szz57gjpbfm9dih23hpspzz11f-openssl-1.0.1f 14 | /nix/store/q784x64hp3nwdxx7lbgb16f74i2bhxxk-glibc-2.18 15 | /nix/store/qw7vn33jcv1yfsfdw19ic5r2jlqk68w3-bash-4.2-p45 16 | /nix/store/skxdffb34mcz50f9q691qsg44bgrxg2x-perl-DBD-SQLite-1.37 17 | /nix/store/vmq6nmnvyblnwlrmhhhpnsjdlri4qz25-curl-7.33.0 18 | /nix/store/xay7d5hfhm9vj3v31dbzimi08ydrgd4w-zlib-1.2.8 19 | /nix/store/xkhr68z09y66c1qwzdq03lnhjl9c51si-perl-WWW-Curl-4.15 20 | /nix/store/ylcpwyczz887grq8lzdz8hn81q7yrn38-gzip-1.6 21 | /nix/store/z1krxp2hwph8fypchf2b0ssnyp6z8k9l-perl-5.16.3 22 | /nix/store/zp4bcz188h69jvrb1qyl10lfkanz7ld3-boehm-gc-7.2d 23 | /nix/store/zsl05mbb69s38bbyi9nfff6vyry9m8jm-gnutar-1.27.1 24 | /nix/store/zxvyl58mw530xf811nmm0i8b6nibwmw5-coreutils-8.21 25 | ----------------------------------------------------------------------------------------------------- 26 | Download of http://cache.nixos.org/nar/00fwcb3janb72b1xf4rnq7ninzmvm8zzzlr6lc8sp9dbl7x838iz.nar.xz finished 27 | -> 24.4 Mib in 0:01:25, average speed 115kib/s 28 | -> writing to ‘/nix/store/94l17wjg65wpkwcm4x51pr5dlvarip6a-gcc-4.8.2’ 29 | Download of http://cache.nixos.org/nar/00fwcb3janb72b1xf4rnq7ninzmvm8zzzlr6lc8sp9dbl7x838iz.nar.xz finished 30 | -> 4.4 Mib in 0:02:23, average speed 333115kib/s 31 | -> writing to ‘/nix/store/6pw49mccs51msj6yrc7hwm88gdkccfd2-sqlite-3.8.0.2’ 32 | Download of http://cache.nixos.org/nar/00fwcb3janb72b1xf4rnq7ninzmvm8zzzlr6lc8sp9dbl7x838iz.nar.xz finished 33 | -> 124.4 Mib in 0:03:23, average speed 1501kib/s 34 | -> writing to ‘/nix/store/zsl05mbb69s38bbyi9nfff6vyry9m8jm-gnutar-1.27.1’ 35 | ----------------------------------------------------------------------------------------------------- 36 | building: 37 | /nix/store/ylcpwyczz887grq8lzdz8hn81q7yrn38-gzip-1.6 38 | /nix/store/j298bijkgdzzv6wlzdidldx297ch5rq2-nix-1.7pre3327_0e2ca26 39 | fetching: 40 | http://cache.nixos.org/nar/0s57kyi85g7lb9irja2cslmh5vc23i4q35dv8pi4gh19k0jc7nf3.nar.xz 50% 23Mib - 1.82 Mib/s 41 | http://cache.nixos.org/nar/07paqfjj437c0mhnkrbli70wlb5liqrnjcid81v66qlmy38r7ygx.nar.xz 10% 2Mib - stalled 42 | http://cache.nixos.org/nar/0s57kyi85g7lb9irja2cslmh5vc23i4q35dv8pi4gh19k0jc7nf3.nar.xz 70% 25Mbib - 2.25 Mib/s 43 | http://cache.nixos.org/nar/0s55vc23i4q35dv8pi4gh19k0jc7nf3.nar.xz..................... 49% 80Mbib - 0.25 byte/s 44 | -------------------------------------------------------------------------------- /nix-build-view/TerminalWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "TerminalWidget.hpp" 2 | 3 | TerminalWidget* 4 | TerminalWidget::Instance() 5 | { 6 | static TerminalWidget* _instance = new TerminalWidget; 7 | return _instance; 8 | } 9 | 10 | int 11 | TerminalWidget::type() const 12 | { 13 | return WidgetName::TerminalWidget; 14 | } 15 | 16 | AdvancedStringContainer 17 | TerminalWidget::render(unsigned int width, unsigned int height) 18 | { 19 | // - m_logfile might obviously have more than 28 rows so only 'render' the 20 | // part we are interested in 21 | 22 | // copy the last h elements from terminal to the out buffer 23 | AdvancedStringContainer out; 24 | 25 | // caches the output for better performance 26 | if ((m_width != width) || (m_height != height)) { 27 | m_width = width; 28 | m_height = height; 29 | AdvancedStringContainer::terminal_rasterize(m_terminal, 30 | m_logfile, 31 | this->width()); 32 | } 33 | 34 | std::vector::const_iterator it_b = m_terminal.begin(); 35 | std::vector::const_iterator it_e = m_terminal.end(); 36 | 37 | if (it_e - height - m_line >= it_b) { 38 | it_b = it_e - height - m_line; 39 | } 40 | 41 | for (unsigned int i = 0; i < height; ++i) { 42 | if (it_b >= it_e) { 43 | break; 44 | } 45 | AdvancedStringContainer t = *it_b++; 46 | out << t; 47 | } 48 | 49 | return out; 50 | } 51 | 52 | void 53 | TerminalWidget::append(AdvancedStringContainer line) 54 | { 55 | // replace all \t with ' ' (8 spaces) 56 | // you can't use copy'n'paste from that terminal, so Makefiles for example will be broken when being copied this way 57 | AdvancedStringContainer buf; 58 | 59 | for (unsigned int i = 0; i < line.size(); ++i) { 60 | AdvancedString a = line[i]; 61 | std::string s = a.str(); 62 | std::stringstream ss; 63 | 64 | for (unsigned int x = 0; x < s.size(); ++x) { 65 | if (s[x] == '\t') { 66 | ss << " "; 67 | } else { 68 | ss << s[x]; 69 | } 70 | } 71 | buf << AdvancedString(ss.str(), 72 | a.fontColor(), 73 | a.attributes(), 74 | a.bgColor()); 75 | } 76 | 77 | // add the new string 78 | m_logfile << buf; 79 | std::vector m; 80 | AdvancedStringContainer::terminal_rasterize(m, buf, this->width()); 81 | 82 | for (unsigned int i = 0; i < m.size(); ++i) { 83 | m_terminal.push_back(m[i]); 84 | } 85 | 86 | if (m_line != 0) { 87 | m_line += m.size(); 88 | } 89 | 90 | update(); 91 | } 92 | 93 | void 94 | TerminalWidget::keyboardInputHandler(int ch) 95 | { 96 | unsigned int a = 0; 97 | 98 | switch(ch) { 99 | case(KEY_HOME): 100 | m_line = m_terminal.size() - height(); 101 | update(); 102 | break; 103 | 104 | case(KEY_END): 105 | m_line = 0; 106 | update(); 107 | break; 108 | 109 | case(KEY_UP): 110 | m_line += 1; 111 | if (m_line > m_terminal.size() - height()) { 112 | m_line = m_terminal.size() - height(); 113 | } 114 | update(); 115 | break; 116 | 117 | case(KEY_DOWN): 118 | if (m_line > 0) { 119 | m_line -= 1; 120 | } 121 | update(); 122 | break; 123 | 124 | case(KEY_PPAGE): 125 | m_line += 15; 126 | if (m_line > m_terminal.size() - height()) { 127 | m_line = m_terminal.size() - height(); 128 | } 129 | update(); 130 | break; 131 | 132 | case(KEY_NPAGE): 133 | a = m_line; 134 | m_line -= 15; 135 | if (m_line > a) { 136 | m_line = 0; 137 | } 138 | update(); 139 | break; 140 | 141 | default: 142 | break; 143 | 144 | } 145 | } 146 | 147 | -------------------------------------------------------------------------------- /nix-build-view/Layout.cpp: -------------------------------------------------------------------------------- 1 | #include "Layout.hpp" 2 | 3 | void 4 | Layout::addWidget(Widget* w, unsigned int heightHint) 5 | { 6 | m_layoutItems.push_back(new LayoutItem(w, heightHint)); 7 | } 8 | 9 | // inputs to update: 10 | // - layout (list of widgets with size hints) 11 | // - widgets whishes (how many lines each widget wants to draw) 12 | // 13 | // layout schaut alle widgets durch: 14 | // - layout: HEIGHT size hint vom layout: FIXED, MINIMUM 1, RESIZE 15 | // - widget: wants height==3, draws nested widgets 16 | // space which is not covered by a widget must be cleared 17 | RasterizedLayout 18 | Layout::rasterize(int width, int height) 19 | { 20 | RasterizedLayout r; 21 | int rowsUsed = 0; 22 | 23 | for (unsigned int i = 0; i < m_layoutItems.size(); ++i) { 24 | FixedWidget fw; 25 | 26 | fw.widget = m_layoutItems[i]->widget; 27 | 28 | fw.height = (fw.widget->rowsWantedByWidget() == 0) ? 0 : 1; 29 | 30 | rowsUsed += fw.height; 31 | fw.width = width; 32 | r.m_fixedWidgets.push_back(fw); 33 | } 34 | 35 | // expand dynamic widgets 36 | if (rowsUsed < height) { 37 | for (unsigned int i = 0; i < r.m_fixedWidgets.size(); ++i) 38 | { 39 | FixedWidget fw = r.m_fixedWidgets[i]; 40 | int type = fw.widget->type(); 41 | 42 | if (type == WidgetName::BuildWidgetManager || 43 | type == WidgetName::FetchWidgetManager) 44 | { 45 | int t = 0; 46 | unsigned int hH = 0; 47 | 48 | // search though the layout's highHint for the widget 49 | for (unsigned int i = 0; i < m_layoutItems.size(); ++i) { 50 | if (m_layoutItems[i]->widget == fw.widget) { 51 | hH = m_layoutItems[i]->heightHint; 52 | } 53 | } 54 | 55 | // use height given by widget unless the layout says it must 56 | // be less 57 | if (fw.widget->rowsWantedByWidget() < hH) { 58 | t = fw.widget->rowsWantedByWidget(); 59 | } else { 60 | t = hH; 61 | } 62 | 63 | // if t now exceeds the available height, we need to limit it 64 | // to what is still available marry x-mess code 65 | if (t > height - rowsUsed) { 66 | t = height - rowsUsed + 1; // FIXME why does +1 work here? 67 | } 68 | 69 | if (t != 0) { 70 | rowsUsed += t - 1; // FIXME why does -1 work here? 71 | } 72 | 73 | fw.height = t; 74 | r.m_fixedWidgets[i] = fw; 75 | } 76 | } 77 | } 78 | 79 | //FIXME this code can be consolidated 80 | // expand dynamic widgets 81 | if (rowsUsed < height) { 82 | for (unsigned int i = 0; i < r.m_fixedWidgets.size(); ++i) { 83 | int type = r.m_fixedWidgets[i].widget->type(); 84 | 85 | if (type == WidgetName::TerminalWidget) { 86 | int n = height - rowsUsed; 87 | r.m_fixedWidgets[i].height += n; 88 | rowsUsed += n; 89 | } 90 | } 91 | } 92 | 93 | // expand help widgets 94 | if (rowsUsed < height) { 95 | for (unsigned int i = 0; i < r.m_fixedWidgets.size(); ++i) { 96 | int type = r.m_fixedWidgets[i].widget->type(); 97 | 98 | if (type == WidgetName::HelpWidget) { 99 | int n = height - rowsUsed; 100 | r.m_fixedWidgets[i].height += n; 101 | rowsUsed += n; 102 | } 103 | } 104 | } 105 | 106 | // expand dynamic widgets 107 | if (rowsUsed < height) { 108 | for (unsigned int i = 0; i < r.m_fixedWidgets.size(); ++i) { 109 | int type = r.m_fixedWidgets[i].widget->type(); 110 | 111 | if (type == WidgetName::VerticalSpacerWidget) { 112 | int n = height - rowsUsed; 113 | r.m_fixedWidgets[i].height += n; 114 | rowsUsed += n; 115 | } 116 | } 117 | } 118 | 119 | return r; 120 | } 121 | 122 | -------------------------------------------------------------------------------- /design-experiments/README: -------------------------------------------------------------------------------- 1 | # motivation 2 | 3 | in this repo i was playing with how to avoid using ncurses but still be able to rewrite several terminal lines... 4 | 5 | which failed so i gave up on this. anyway: it would be great to be able to do so as this would give rise to very cool programs like a multiline curl download or similar stuff... 6 | 7 | i continued using ncruses but rewrote the string handling to have colored strings which was really nasty business. 8 | 9 | 10 | # details 11 | 12 | i first tried to use terminal CTRLs to avoid ncurses but that failed since i couldn't access data outside the view (shift+pgup or scroll-wheel when using X). 13 | now i'm heading for a ncurses client which works like 'less' and after calling ctrl+c (or normal program exit) it will print the buffer to the terminal. 14 | 15 | it is planned to be called like: 16 | 17 | nix-build -A foo -JSON | nix-build-viewer 18 | 19 | current experiments: 20 | - [done] find out if selection of text can be preserved 21 | when not using wclear(win); it can be done but only if the selected field is not modified 22 | - make dynamic coloring work 23 | - create a working mainloop 24 | - [done] keyboard input 25 | - [done] terminal resize events using KEY_RESIZE 26 | - receives CTRL+C, since i might have to forward this event and afterwards exit 27 | - use pselect instead of while(true) and react only on incomming data, wrap getchar as well! 28 | - receives data using stdin via JSON requests 29 | - stdout/stderr from nix-build is displayed in a log, JSON strings are translated into the statusbuf 30 | - create a widget library where elements can be selected, expanded and collapsed 31 | - create a logger widget with search support 32 | 33 | 34 | 35 | 36 | # todo 37 | 38 | what to print to the statusline? 39 | - DerivationGoal 40 | could print the current status it is in: fetching|buildPhase|installPhase|... and it could 41 | pass a logfile, which one can attach to with 'tail -f /tmp/9brfbp9gk7x3qim52q49rckhw7vw08h2-asciidoc-8.6.8/buildlog' 42 | /nix/store/9brfbp9gk7x3qim52q49rckhw7vw08h2-asciidoc-8.6.8.drv [buildPhase] since 5min 20sec ETA: ~ 20min 43 | log: /tmp/9brfbp9gk7x3qim52q49rckhw7vw08h2-asciidoc-8.6.8/buildlog 44 | - SubstitutionGoal 45 | download progress while downloading | extracting stuff 46 | 47 | * design phase 48 | 49 | * [done] design a multiline prototype -> see demo.cpp/demo2.cpp 50 | 51 | this can only be used if the amount of rows (used by the statusline) don't exceed the amount of rows shown by the terminal. 52 | as my list might get pretty big (10-200 entries) i will not continue to use this and instead use ncurses like done in htop. 53 | 54 | still if one has only 1-3 statuslines it should be possible to be used without many issues. 55 | 56 | * [done] disable keyboard echo 57 | 58 | http://stackoverflow.com/questions/4963421/vt-terminal-disable-local-editing-and-echo 59 | 60 | * [done] make the widgets resizeable - signal 28 61 | 62 | * [done] single line 'compact mode' prototype (wanted by eelco) - see demo3.cpp and screenshot3.jpeg 63 | 64 | * create shortcuts which can affect the console rendering verbosity 65 | 66 | * 'v' switch between verbose and compact mode (single line vs multiline) 67 | 68 | * when 3 widgets are rendered (without line-wrapping) resizing works as expected, however, if a widget exceeded the linewidth, then 69 | the clearStatus call does not remove the correct ammount of lines and leaves unwanted stuff 70 | 71 | * a fix would be to compute how many linewraps resulted from the resize: previous widget-width / new linewidth = 2,173 means we have to remove 72 | 3 lines now, instead of just one for example 73 | 74 | * add regexp autocolor feature 75 | 76 | * look at: 'nixos-rebuild build -Q', why does it look so nice? 77 | 78 | * find out what features can be supported: 79 | 80 | * single download sizes 81 | * total download size 82 | * single bandwidth per download 83 | * total badwidth per all downloads 84 | * data already downloaded 85 | * maybe include the hierarchy of targets? 86 | 87 | * find out how downloads are started when doing 'nixos-rebuild switch' -> nix-build 88 | 89 | * what drives the main loop in 'nix-build' 90 | nix-store binary (nix-store.c) calls void run(..) -> opRealise(..) -> store->buildPaths(..) -> (build.cc) -> void Worker::run(const Goals & _topGoals) is the main loop! 91 | 92 | * find a nice output wrapper, preferably a singleton which wraps printMsg() somehow 93 | 94 | * how does nix-build write to the shell 95 | 96 | it uses libutil/util.cc's: 97 | printMsg() -> printMsg_() -> writeToStderr(s) -> _writeToStderr() -> defaultWriteToStderr(..) -> writeFull(..) -> write(..) 98 | 99 | * how to integrate the WINCH (signal 28) terminal signal which is thrown on console resize? 100 | 101 | probably using a FD which the sinal handler writes to 102 | 103 | * how to extend ./scripts/download-from-binary-cache.pl.in to get a periodict download status update? 104 | 105 | https://metacpan.org/source/SYP/Net-Curl-0.33/lib/Net/Curl/Easy.pm#L14 106 | 107 | # related 108 | 109 | * http://stackoverflow.com/questions/22476911/ansi-terminal-rewrite-the-output 110 | -------------------------------------------------------------------------------- /nix-build-view/WindowManager.cpp: -------------------------------------------------------------------------------- 1 | #include "WindowManager.hpp" 2 | #include "Widget.hpp" 3 | #include "Layout.hpp" 4 | #include "AdvancedString.hpp" 5 | 6 | #include "FetchWidget.hpp" 7 | #include "TerminalWidget.hpp" 8 | #include "BuildWidget.hpp" 9 | #include "StatusWidget.hpp" 10 | #include "HelpWidget.hpp" 11 | #include "VerticalSpacerWidget.hpp" 12 | #include "FetchWidgetManager.hpp" 13 | #include "BuildWidgetManager.hpp" 14 | 15 | #include 16 | 17 | WindowManager* 18 | WindowManager::Instance() 19 | { 20 | static WindowManager* _instance = new WindowManager(stdscr); 21 | return _instance; 22 | } 23 | 24 | WindowManager::WindowManager(WINDOW* win) 25 | { 26 | m_win = win; 27 | 28 | struct winsize size; 29 | if (ioctl(0, TIOCGWINSZ, (char *) &size) < 0) { 30 | printf("TIOCGWINSZ error"); 31 | } 32 | 33 | m_width = size.ws_col; 34 | m_height = size.ws_row; 35 | 36 | statusWidget = StatusWidget::Instance(); 37 | verticalSpacer = new VerticalSpacerWidget(); 38 | Layout* l1 = new Layout; 39 | 40 | helpWidget = new HelpWidget(); 41 | 42 | l1->addWidget(helpWidget); 43 | l1->addWidget(verticalSpacer, 0); 44 | l1->addWidget(statusWidget); 45 | 46 | Layout* l2 = new Layout; 47 | 48 | TerminalWidget* terminalWidget = TerminalWidget::Instance(); 49 | l2->addWidget(terminalWidget); 50 | 51 | BuildWidgetManager* buildWidgetManager = BuildWidgetManager::Instance(); 52 | FetchWidgetManager* fetchWidgetManager = FetchWidgetManager::Instance(); 53 | 54 | l2->addWidget(buildWidgetManager, 4); 55 | l2->addWidget(fetchWidgetManager, 4); 56 | 57 | l2->addWidget(statusWidget); 58 | 59 | Layout* l3 = new Layout; 60 | l3->addWidget(terminalWidget); 61 | l3->addWidget(statusWidget); 62 | 63 | Layout* l4 = new Layout; 64 | l4->addWidget(verticalSpacer,0); 65 | l4->addWidget(fetchWidgetManager,4000); 66 | l4->addWidget(statusWidget); 67 | 68 | Layout* l5 = new Layout; 69 | l5->addWidget(verticalSpacer,0); 70 | l5->addWidget(buildWidgetManager,4000); 71 | l5->addWidget(statusWidget); 72 | 73 | addLayout(l1); 74 | addLayout(l2); 75 | addLayout(l3); 76 | addLayout(l4); 77 | addLayout(l5); 78 | 79 | setLayout(1); 80 | setKeyboardInputHandler(TerminalWidget::Instance()); 81 | } 82 | 83 | void 84 | WindowManager::addLayout(Layout* l) 85 | { 86 | m_layouts.push_back(l); 87 | } 88 | 89 | // layout widgets on the canvas to later draw them 90 | void 91 | WindowManager::setLayout(int layout) 92 | { 93 | m_selectedLayout = layout; 94 | } 95 | 96 | void 97 | WindowManager::resize(int width, int height) 98 | { 99 | m_width = width; 100 | m_height = height; 101 | update(); 102 | } 103 | 104 | // redraw the whole screen 105 | void 106 | WindowManager::update(Widget* w) 107 | { 108 | //FIXME do not clear and redraw the whole screen 109 | wclear(m_win); 110 | if (w == NULL) { 111 | ; //FIXME redraw everything 112 | } else { 113 | ;//FIXME redraw only single widget space 114 | } 115 | 116 | int pos = 0; 117 | int heightpointer = 0; 118 | 119 | attron(A_REVERSE); 120 | 121 | if (m_selectedLayout > m_layouts.size()) { 122 | return; 123 | } 124 | 125 | Layout* l = m_layouts[m_selectedLayout]; 126 | 127 | // layout the widget in the current terminal width/height 128 | RasterizedLayout r = l->rasterize(width(), height()); 129 | 130 | for (unsigned int i = 0; i < r.m_fixedWidgets.size(); ++i) { 131 | FixedWidget fw = r.m_fixedWidgets[i]; 132 | AdvancedStringContainer as = fw.widget->render(fw.width, fw.height); 133 | int left = fw.width * fw.height; 134 | 135 | pos = 0; 136 | for (unsigned int x = 0; x < as.size(); ++x) { 137 | if(!left) { 138 | break; 139 | } 140 | 141 | std::string s; 142 | // limit the width of the string to the max of width*height 143 | if (left < as[x].size()) { 144 | s = as[x].str().substr(0, left); 145 | } else { 146 | s = as[x].str(); 147 | } 148 | 149 | attron(as[x].attributes() | 150 | COLOR_PAIR(cm.setColor(as[x].bgColor(), as[x].fontColor())) 151 | ); 152 | 153 | mvprintw(heightpointer + (pos / width()), pos % width(), s.c_str()); 154 | 155 | pos += s.size(); 156 | left -= s.size(); 157 | 158 | attroff(as[x].attributes() | 159 | COLOR_PAIR(cm.setColor(as[x].bgColor(), as[x].fontColor())) 160 | ); 161 | } 162 | 163 | heightpointer += fw.height; 164 | } 165 | } 166 | 167 | void 168 | WindowManager::keyboardInputHandler(int ch) 169 | { 170 | /////////// BEGIN global shortcuts ////////////////////////// 171 | if (ch == 'Q' || ch == 'q') { 172 | main_loop = 0; 173 | return; 174 | } 175 | /////////// END global shortcuts ////////////////////////// 176 | 177 | // 0 - help widget 178 | // 1 - composed view: input should be forwarded to the log 179 | // 2 - like 1 but log has fullscreen 180 | // 3 - fetch is fullscreen and is scrollable 181 | // 4 - build is fullscreen and is scrollable 182 | if (ch == '1' || ch == '2' || ch == '3' || ch == '4') { 183 | int w = ch - 48; 184 | setLayout(w); 185 | statusWidget->setFocus(w); 186 | 187 | switch(w) { 188 | case 1: 189 | case 2: 190 | setKeyboardInputHandler(TerminalWidget::Instance()); 191 | break; 192 | 193 | case 3: 194 | setKeyboardInputHandler(FetchWidgetManager::Instance()); 195 | break; 196 | 197 | case 4: 198 | setKeyboardInputHandler(BuildWidgetManager::Instance()); 199 | break; 200 | 201 | default: 202 | setKeyboardInputHandler(NULL); 203 | }; 204 | 205 | return; 206 | } 207 | 208 | if (ch == 'h' || ch == 'H') { 209 | setLayout(0); 210 | statusWidget->setFocus(0); 211 | return; 212 | } 213 | 214 | // this event indicates a SIG 28 - SIGWINCH 215 | if (ch == KEY_RESIZE) { 216 | struct winsize size; 217 | 218 | if (ioctl(0, TIOCGWINSZ, (char *) &size) < 0) { 219 | printf("TIOCGWINSZ error"); 220 | } 221 | 222 | resize(size.ws_col, size.ws_row); 223 | return; 224 | } 225 | 226 | if (m_focusWidget) { 227 | m_focusWidget->keyboardInputHandler(ch); 228 | } 229 | } 230 | 231 | void 232 | WindowManager::setKeyboardInputHandler(Widget* w) 233 | { 234 | m_focusWidget = w; 235 | } 236 | 237 | std::string 238 | WindowManager::version() 239 | { 240 | return std::string(VERSION); 241 | } 242 | 243 | int 244 | WindowManager::EventLoop() 245 | { 246 | // if (FetchWidgetManager::Instance()-> 247 | //FIXME check if all builds/fetches were completed and afterwards quit 248 | return main_loop; 249 | } 250 | 251 | -------------------------------------------------------------------------------- /design-experiments/demo3.cpp: -------------------------------------------------------------------------------- 1 | // http://infohost.nmt.edu/~eweiss/222_book/222_book/0201433079/ch18lev1sec12.html 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include /* printf */ 12 | #include /* signal, raise, sig_atomic_t */ 13 | 14 | #include 15 | #ifndef TIOCGWINSZ 16 | #include 17 | #endif 18 | 19 | 20 | using namespace std; 21 | 22 | #define CURSOR_TO_START_OF_LINE "\r" 23 | #define CURSOR_UP_ONE_LINE "\e[A" 24 | #define CURSOR_CLEAR_TO_EOL "\e[K" 25 | #define CURSOR_CLEAN_ALL_AFTERWARDS "\e[J" 26 | 27 | #define RESET "\033[0m" 28 | #define BLACK "\033[30m" /* Black */ 29 | #define RED "\033[31m" /* Red */ 30 | #define GREEN "\033[32m" /* Green */ 31 | #define YELLOW "\033[33m" /* Yellow */ 32 | #define BLUE "\033[34m" /* Blue */ 33 | #define MAGENTA "\033[35m" /* Magenta */ 34 | #define CYAN "\033[36m" /* Cyan */ 35 | #define WHITE "\033[37m" /* White */ 36 | #define BOLDBLACK "\033[1m\033[30m" /* Bold Black */ 37 | #define BOLDRED "\033[1m\033[31m" /* Bold Red */ 38 | #define BOLDGREEN "\033[1m\033[32m" /* Bold Green */ 39 | #define BOLDYELLOW "\033[1m\033[33m" /* Bold Yellow */ 40 | #define BOLDBLUE "\033[1m\033[34m" /* Bold Blue */ 41 | #define BOLDMAGENTA "\033[1m\033[35m" /* Bold Magenta */ 42 | #define BOLDCYAN "\033[1m\033[36m" /* Bold Cyan */ 43 | #define BOLDWHITE "\033[1m\033[37m" /* Bold White */ 44 | 45 | sig_atomic_t signaled = 0; 46 | int t = 3; 47 | 48 | void my_handler (int param) 49 | { 50 | signaled = 1; 51 | } 52 | 53 | static int linecount = 0; 54 | 55 | class Widget { 56 | public: 57 | std::string render(); 58 | }; 59 | 60 | class FetchWidget : public Widget { 61 | private: 62 | std::string m_name; 63 | float m_percent; 64 | int m_bits_per_sec; 65 | public: 66 | FetchWidget(std::string url, float percent, int bits_per_sec) { 67 | m_name=url; 68 | m_percent = percent; 69 | m_bits_per_sec = bits_per_sec; 70 | }; 71 | std::string render() { 72 | std::stringstream s; 73 | 74 | int size = m_name.size(); 75 | std::stringstream url_progress; 76 | float end = m_percent * size; 77 | url_progress << CYAN << m_name.substr(0, (int)end) << RESET << m_name.substr((int)end, size-(int)end); 78 | 79 | s << url_progress.str() << " " << (int)(m_percent*100) << "% " << m_bits_per_sec << "\n"; 80 | return s.str(); 81 | } 82 | }; 83 | 84 | void clearStatus() { 85 | for (int i=0; i < linecount; i++) { 86 | //printf(CURSOR_TO_START_OF_LINE); 87 | printf(CURSOR_UP_ONE_LINE); 88 | //printf(CURSOR_CLEAR_TO_EOL); 89 | } 90 | printf(CURSOR_CLEAN_ALL_AFTERWARDS); 91 | } 92 | 93 | 94 | void drawStatus(int foo) { 95 | float fa = (((float)foo/5)*0.8) < 1.0 ? ((float)foo/5)*0.8 : 1.0; 96 | float fb = (((float)foo/5)*0.5) < 1.0 ? ((float)foo/5)*0.5 : 1.0; 97 | float fc = (((float)foo/5)*1.7) < 1.0 ? ((float)foo/5)*1.7 : 1.0; 98 | 99 | clearStatus(); 100 | if (foo == 3 || foo == 7 || foo == 10) 101 | std::cout << " Download of " << CYAN << "http://cache.nixos.org/nar/00fwcb3janb72b1xf4rnq7ninzmvm8zzzlr6lc8sp9dbl7x838iz.nar.xz" << RESET << " finished\n" << 102 | " -> 24.4 Mib in 0:01:25, average speed 115 kib/s\n" << 103 | " -> writing to ‘/nix/store/94l17wjg65wpkwcm4x51pr5dlvarip6a-" << CYAN << "gcc-4.8.2" << RESET << "’\n"; 104 | 105 | std::stringstream ssout; 106 | if (foo < 10) 107 | ssout << MAGENTA << "| building:" << " 2/8 targets " << RESET; 108 | 109 | if (foo == 3) t=2; 110 | if (foo == 7) t=1; 111 | if (foo == 10) t=0; 112 | 113 | if (foo < 10) ssout << GREEN << "| fetching: " << t << "/" << t << " targets - " << RESET << YELLOW << "23.3 kib/s" << RESET << "\n"; 114 | 115 | std::string sout = ssout.str(); 116 | std::cout << sout; 117 | int c = 0; 118 | for (int i=0 ; i <= sout.size(); ++i) 119 | if (sout[i] == '\n') 120 | c++; 121 | linecount = c; 122 | } 123 | 124 | static void pr_winsize(int fd) 125 | { 126 | struct winsize size; 127 | 128 | if (ioctl(fd, TIOCGWINSZ, (char *) &size) < 0) 129 | printf("TIOCGWINSZ error"); 130 | printf("%d rows, %d columns\n", size.ws_row, size.ws_col); 131 | } 132 | 133 | void signal_callback_handler(int signum) { 134 | printf("Caught signal %d\n",signum); 135 | 136 | //std::cout << GetEnv("PATH") << std::endl; 137 | pr_winsize(STDIN_FILENO); 138 | 139 | exit(signum); 140 | } 141 | 142 | 143 | int main() { 144 | int foo = 0; 145 | signal(SIGWINCH, signal_callback_handler); 146 | 147 | std::cout << 148 | "building Nix...\n" << 149 | "these derivations will be " << MAGENTA << "built" << RESET << ":\n" << 150 | " /nix/store/4hh3935anmmhl13q8lmdkgdsxzh46gq6-" << MAGENTA << "tig-1.1.drv\n" << RESET << 151 | " /nix/store/9brfbp9gk7x3qim52q49rckhw7vw08h2-" << MAGENTA << "asciidoc-8.6.8.drv\n" << RESET << 152 | " /nix/store/ckn6dnnb0ayjbdn3avpqvqa5rs35k2w1-" << MAGENTA << "tig-1.1.tar.gz.drv\n" << RESET << 153 | " /nix/store/j9bgfp4q6h8gdi4b6idvi1r39b5hagvz-" << MAGENTA << "asciidoc-8.6.8.tar.gz.drv\n" << RESET << 154 | "these paths will be " << GREEN << "fetched" << RESET << " (40.1 Mib download, 201.66 Mib unpacked):\n" << RESET << 155 | " /nix/store/0yzz6p08k1sgpdb63c0wx48vx0yc51g6-" << GREEN << "bzip2-1.0.6\n" << RESET << 156 | " /nix/store/1a08qk5q5vdfv13rwasbf4fqa2s26kx4-" << GREEN << "attr-2.4.47\n" << RESET << 157 | " /nix/store/3amm865b2qb5s5mwvshvd9kpfq3aj1bc-" << GREEN << "libssh2-1.4.3\n" << RESET << 158 | " /nix/store/5myfmphlck9gcabr6czlg6792d9zhh4m-" << GREEN << "perl-DBI-1.630\n" << RESET << 159 | " /nix/store/j298bijkgdzzv6wlzdidldx297ch5rq2-" << GREEN << "nix-1.7pre3327_0e2ca\n" << RESET << 160 | " /nix/store/mad928szz57gjpbfm9dih23hpspzz11f-" << GREEN << "openssl-1.0.1f\n" << RESET << 161 | " /nix/store/q784x64hp3nwdxx7lbgb16f74i2bhxxk-" << GREEN << "glibc-2.18\n" << RESET << 162 | " /nix/store/qw7vn33jcv1yfsfdw19ic5r2jlqk68w3-" << GREEN << "bash-4.2-p45\n" << RESET << 163 | " /nix/store/skxdffb34mcz50f9q691qsg44bgrxg2x-" << GREEN << "perl-DBD-SQLite-1.37\n" << RESET << 164 | " /nix/store/vmq6nmnvyblnwlrmhhhpnsjdlri4qz25-" << GREEN << "curl-7.33.0\n" << RESET << 165 | " /nix/store/xay7d5hfhm9vj3v31dbzimi08ydrgd4w-" << GREEN << "zlib-1.2.8\n" << RESET << 166 | " /nix/store/xkhr68z09y66c1qwzdq03lnhjl9c51si-" << GREEN << "perl-WWW-Curl-4.15\n" << RESET << 167 | " /nix/store/ylcpwyczz887grq8lzdz8hn81q7yrn38-" << GREEN << "gzip-1.6\n" << RESET << 168 | " /nix/store/z1krxp2hwph8fypchf2b0ssnyp6z8k9l-" << GREEN << "perl-5.16.3\n" << RESET << 169 | " /nix/store/zp4bcz188h69jvrb1qyl10lfkanz7ld3-" << GREEN << "boehm-gc-7.2d\n" << RESET << 170 | " /nix/store/zsl05mbb69s38bbyi9nfff6vyry9m8jm-" << GREEN << "gnutar-1.27.1\n" << RESET << 171 | " /nix/store/zxvyl58mw530xf811nmm0i8b6nibwmw5-" << GREEN << "coreutils-8.21\n" << RESET << 172 | "-----------------------------\n"; 173 | 174 | while(1) { 175 | drawStatus(foo); 176 | if(foo==10) { 177 | clearStatus(); 178 | exit(0); 179 | } 180 | foo+=1; 181 | sleep(1); 182 | } 183 | } 184 | 185 | -------------------------------------------------------------------------------- /nix-build-view/TDD-nix-build.cpp: -------------------------------------------------------------------------------- 1 | #include "TDD-nix-build.hpp" 2 | 3 | #include "FetchWidgetManager.hpp" 4 | #include "BuildWidgetManager.hpp" 5 | #include "FetchWidget.hpp" 6 | #include "BuildWidget.hpp" 7 | #include "TerminalWidget.hpp" 8 | #include "WindowManager.hpp" 9 | 10 | #include 11 | #include 12 | 13 | const std::string alphanum = 14 | "0123456789" 15 | "abcdefghijklmnopqrstuvwxyz"; 16 | 17 | std::string 18 | randomString(unsigned int size) 19 | { 20 | std::string str; 21 | for (unsigned int i = 0; i < size; ++i) { 22 | str += alphanum[rand()%alphanum.size()]; 23 | } 24 | 25 | return str; 26 | } 27 | 28 | NixBuild::NixBuild() 29 | { 30 | srand(10); 31 | m.push_back("acpi-1.7"); 32 | m.push_back("aescrypt-3.0.9"); 33 | m.push_back("anki-2.0.22"); 34 | m.push_back("aria2-1.18.1"); 35 | m.push_back("audacity-2.0.5"); 36 | m.push_back("autossh-1.4c"); 37 | m.push_back("basket-1.81"); 38 | m.push_back("bc-1.06"); 39 | m.push_back("blender-2.70"); 40 | m.push_back("brickd-1.0.11"); 41 | m.push_back("brickv-1.1.14"); 42 | m.push_back("bsddb3-6.0.1"); 43 | m.push_back("byobu-5.68"); 44 | m.push_back("calibre-1.31.0"); 45 | m.push_back("davfs2-1.4.7"); 46 | m.push_back("ddd-3.3.12"); 47 | m.push_back("dhex-0.68"); 48 | m.push_back("duplicity-0.6.23"); 49 | m.push_back("elinks-0.12pre6"); 50 | m.push_back("emacs-24.3"); 51 | m.push_back("encfs-1.7.4"); 52 | m.push_back("env-opencv-fishing"); 53 | m.push_back("env-srmg"); 54 | m.push_back("evopedia-0.4.2"); 55 | m.push_back("ffmpeg-2.2.1"); 56 | m.push_back("file-5.17"); 57 | m.push_back("gajim-0.15.4"); 58 | m.push_back("gdb-7.7"); 59 | m.push_back("gimp-2.8.10"); 60 | m.push_back("git-1.9.2"); 61 | m.push_back("glxinfo-8.1.0"); 62 | m.push_back("gnupg-2.0.22"); 63 | m.push_back("gnuplot-4.6.3"); 64 | m.push_back("gobby-0.4.94"); 65 | m.push_back("gpgme-1.4.3"); 66 | m.push_back("gqview-2.1.5"); 67 | m.push_back("htop-1.0.2"); 68 | m.push_back("ikiwiki-3.20140227"); 69 | m.push_back("inkscape-0.48.4"); 70 | m.push_back("inotify-tools-3.13"); 71 | m.push_back("iotop-0.6"); 72 | m.push_back("iperf-2.0.5"); 73 | m.push_back("irssi-0.8.15"); 74 | m.push_back("kdiff3-0.9.97"); 75 | m.push_back("keepassx-2.0alpha5"); 76 | m.push_back("ktorrent-4.3.1"); 77 | m.push_back("libmcrypt-2.5.8"); 78 | m.push_back("libnoise-1.0.0"); 79 | m.push_back("libreoffice-4.0.5.2"); 80 | m.push_back("lightning-1.2c"); 81 | m.push_back("lsof-4.87"); 82 | m.push_back("mcrypt-2.6.8"); 83 | m.push_back("mplayer-1.1"); 84 | m.push_back("nmap-6.40"); 85 | m.push_back("openssl-1.0.1g"); 86 | m.push_back("p7zip-9.20.1"); 87 | m.push_back("patchelf-0.8"); 88 | m.push_back("pavucontrol-2.0"); 89 | m.push_back("phantomjs-1.9.7-4"); 90 | m.push_back("picocom-1.7"); 91 | m.push_back("pinentry-0.8.3"); 92 | m.push_back("psi-0.15-rc3"); 93 | m.push_back("pwgen-2.06"); 94 | m.push_back("qgit-2.5"); 95 | m.push_back("rdiff-backup-1.3.3"); 96 | m.push_back("rsync-3.1.0"); 97 | m.push_back("rtmpdump-2.4"); 98 | m.push_back("seahorse-3.10.2"); 99 | m.push_back("send_udp-1.0.0"); 100 | m.push_back("skype-4.2.0.11"); 101 | m.push_back("smokegen-4.12.4"); 102 | m.push_back("sox-14.4.1"); 103 | m.push_back("sqlite-3.8.0.2-interactive"); 104 | m.push_back("sshfs-fuse-2.5"); 105 | m.push_back("stardict-3.0.3"); 106 | m.push_back("subversion-1.8.8"); 107 | m.push_back("thunderbird-17.0.11esr"); 108 | m.push_back("tig-1.2.1"); 109 | m.push_back("tightvnc-1.3.10"); 110 | m.push_back("timidity-2.13.0"); 111 | m.push_back("tmux-1.8"); 112 | m.push_back("unrar-5.1.2"); 113 | m.push_back("unzip-6.0"); 114 | m.push_back("vim_configurable-7.4.23"); 115 | m.push_back("vlc-2.1.4"); 116 | m.push_back("wgetpaste-2.23"); 117 | m.push_back("which-2.20"); 118 | m.push_back("wireshark-1.11.2"); 119 | m.push_back("xbmc-12.3"); 120 | m.push_back("xclip-0.12"); 121 | m.push_back("xev-1.2.1"); 122 | m.push_back("xinput-1.6.1"); 123 | m.push_back("xset-1.2.3"); 124 | m.push_back("xz-5.0.5"); 125 | m.push_back("youtube-dl-2014.04.02"); 126 | m.push_back("zip-3.0"); 127 | 128 | AdvancedStringContainer s; 129 | 130 | s << AdvancedString("this is the nix-build-view UNIT TEST, no real downloads are made, no bandwidth is harmed!\n"); 131 | 132 | s << AdvancedString("building Nix...\n"); 133 | s << AdvancedString("these paths will be ") 134 | << AdvancedString("fetched", COLOR_GREEN) 135 | << AdvancedString(" (") 136 | << AdvancedString("40.1", COLOR_YELLOW) 137 | << AdvancedString(" Mib download, ") 138 | << AdvancedString("201.66", COLOR_YELLOW) 139 | << AdvancedString(" Mib unpacked):\n"); 140 | 141 | // this function is very very slow but since it is only for testing, who cares! you just need to know that! 142 | for (int i = 0; i < 555; ++i) { 143 | std::string n ="/nix/store/"; 144 | n += randomString(44); 145 | n += "-"; 146 | n += m[rand() % m.size()]; 147 | n += ".nar.xz"; 148 | 149 | m_fetch.push_back(n); 150 | FetchWidgetManager::Instance()->addFetch(n, 0.01f, rand()); 151 | s << " " << AdvancedString(n, COLOR_GREEN) << "\n"; 152 | } 153 | 154 | std::vector b; 155 | b.push_back("preConfigurePhase"); 156 | b.push_back("configurePhase"); 157 | b.push_back("buildPhase"); 158 | b.push_back("installationPhase"); 159 | b.push_back("postinstallationPhase"); 160 | 161 | s << AdvancedString("these derivations will be ") 162 | << AdvancedString("built", COLOR_MAGENTA) 163 | << AdvancedString(":\n"); 164 | 165 | for (int i = 0; i < 555; ++i) { 166 | std::string n = "/nix/store/"; 167 | n += randomString(44); 168 | n += "-"; 169 | n += m[rand() % m.size()]; 170 | BuildWidgetManager::Instance()->addBuild(n, b); 171 | m_build.push_back(n); 172 | s << " " << AdvancedString(n, COLOR_MAGENTA) << "\n"; 173 | } 174 | 175 | s << "\n"; 176 | 177 | s << "Now follows the log:\n"; 178 | 179 | TerminalWidget::Instance()->append(s); 180 | } 181 | 182 | // updates a random amount (range [0, 100]) of elements about every 1000 ms 183 | void 184 | NixBuild::tick() 185 | { 186 | // FIXME move this code from the data source to the render routine 187 | timeval time; 188 | gettimeofday(&time, NULL); 189 | long millis_now = (time.tv_sec * 1000) + (time.tv_usec / 1000); 190 | 191 | static bool ff = true; 192 | 193 | if (ff) { 194 | ff = false; 195 | millis_old = millis_now; 196 | } 197 | 198 | // update all values only about each second 199 | if (abs((int)(millis_old - millis_now)) < 1000) { 200 | return; 201 | } 202 | 203 | // AdvancedStringContainer v; 204 | // v << "f: "<< (int)(millis_now) << " bar "<< abs((int)(millis_old - millis_now)) << "\n"; 205 | // TerminalWidget::Instance()->append(v); 206 | 207 | millis_old = millis_now; 208 | 209 | int elements = rand() % m_fetch.size(); 210 | 211 | for (int i = 0; i < elements; ++i) { 212 | int e = rand() % m_fetch.size(); 213 | float g = (float(rand() % 100) / 100) + 1.0f; 214 | float f = FetchWidgetManager::Instance()->getProgress(m_fetch[e]) * g; 215 | 216 | if (f > 1.0) { 217 | f = 1.0f; 218 | } 219 | 220 | if (f < 0) { 221 | f = 0.0f; 222 | } 223 | 224 | FetchWidgetManager::Instance()->setProgress(m_fetch[e], f); 225 | } 226 | 227 | int elements2 = rand() % m_build.size(); 228 | 229 | for (int i = 0; i < elements2; ++i) { 230 | int e = rand() % m_build.size(); 231 | int f = BuildWidgetManager::Instance()->getPhase(m_build[e]); 232 | 233 | BuildWidgetManager::Instance()->setPhase(m_build[e], f + 1); 234 | } 235 | } 236 | 237 | -------------------------------------------------------------------------------- /design-experiments/demo.cpp: -------------------------------------------------------------------------------- 1 | // http://infohost.nmt.edu/~eweiss/222_book/222_book/0201433079/ch18lev1sec12.html 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include /* printf */ 12 | #include /* signal, raise, sig_atomic_t */ 13 | 14 | #include 15 | #ifndef TIOCGWINSZ 16 | #include 17 | #endif 18 | 19 | 20 | using namespace std; 21 | 22 | #define CURSOR_TO_START_OF_LINE "\r" 23 | #define CURSOR_UP_ONE_LINE "\e[A" 24 | #define CURSOR_CLEAR_TO_EOL "\e[K" 25 | #define CURSOR_CLEAN_ALL_AFTERWARDS "\e[J" 26 | 27 | #define RESET "\033[0m" 28 | #define BLACK "\033[30m" /* Black */ 29 | #define RED "\033[31m" /* Red */ 30 | #define GREEN "\033[32m" /* Green */ 31 | #define YELLOW "\033[33m" /* Yellow */ 32 | #define BLUE "\033[34m" /* Blue */ 33 | #define MAGENTA "\033[35m" /* Magenta */ 34 | #define CYAN "\033[36m" /* Cyan */ 35 | #define WHITE "\033[37m" /* White */ 36 | #define BOLDBLACK "\033[1m\033[30m" /* Bold Black */ 37 | #define BOLDRED "\033[1m\033[31m" /* Bold Red */ 38 | #define BOLDGREEN "\033[1m\033[32m" /* Bold Green */ 39 | #define BOLDYELLOW "\033[1m\033[33m" /* Bold Yellow */ 40 | #define BOLDBLUE "\033[1m\033[34m" /* Bold Blue */ 41 | #define BOLDMAGENTA "\033[1m\033[35m" /* Bold Magenta */ 42 | #define BOLDCYAN "\033[1m\033[36m" /* Bold Cyan */ 43 | #define BOLDWHITE "\033[1m\033[37m" /* Bold White */ 44 | 45 | sig_atomic_t signaled = 0; 46 | 47 | void my_handler (int param) 48 | { 49 | signaled = 1; 50 | } 51 | 52 | static int linecount = 0; 53 | 54 | class Widget { 55 | public: 56 | std::string render(); 57 | }; 58 | 59 | class FetchWidget : public Widget { 60 | private: 61 | std::string m_name; 62 | float m_percent; 63 | int m_bits_per_sec; 64 | public: 65 | FetchWidget(std::string url, float percent, int bits_per_sec) { 66 | m_name=url; 67 | m_percent = percent; 68 | m_bits_per_sec = bits_per_sec; 69 | }; 70 | std::string render() { 71 | std::stringstream s; 72 | 73 | int size = m_name.size(); 74 | std::stringstream url_progress; 75 | float end = m_percent * size; 76 | url_progress << CYAN << m_name.substr(0, (int)end) << RESET << m_name.substr((int)end, size-(int)end); 77 | 78 | s << url_progress.str() << " " << (int)(m_percent*100) << "% " << m_bits_per_sec << "\n"; 79 | return s.str(); 80 | } 81 | }; 82 | 83 | void clearStatus() { 84 | for (int i=0; i < linecount; i++) { 85 | //printf(CURSOR_TO_START_OF_LINE); 86 | printf(CURSOR_UP_ONE_LINE); 87 | //printf(CURSOR_CLEAR_TO_EOL); 88 | } 89 | printf(CURSOR_CLEAN_ALL_AFTERWARDS); 90 | } 91 | 92 | 93 | void drawStatus(int foo) { 94 | float fa = (((float)foo/5)*0.8) < 1.0 ? ((float)foo/5)*0.8 : 1.0; 95 | float fb = (((float)foo/5)*0.5) < 1.0 ? ((float)foo/5)*0.5 : 1.0; 96 | float fc = (((float)foo/5)*1.7) < 1.0 ? ((float)foo/5)*1.7 : 1.0; 97 | FetchWidget urlW0("http://cache.nixos.org/nar/0s57kyi85g7lb9irja2cslmh5vc23i4q35dv8pi4gh1-foobar-1.2.3.nar.xz", fa, 12356); 98 | FetchWidget urlW1("http://cache.nixos.org/nar/0s57kyi85g7lb9irja3i4q35dv8pi4gh1-foobar-1.2.3.nar.xz .........", fb, 12356); 99 | FetchWidget urlW2("http://cache.nixos.org/nar/0s57kyi8-foobar-1.2.3.nar.xz ..................................", fc, 12356); 100 | 101 | clearStatus(); 102 | if (foo == 3 || foo == 7 || foo == 10) 103 | std::cout << " Download of " << CYAN << "http://cache.nixos.org/nar/00fwcb3janb72b1xf4rnq7ninzmvm8zzzlr6lc8sp9dbl7x838iz.nar.xz" << RESET << " finished\n" << 104 | " -> 24.4 Mib in 0:01:25, average speed 115 kib/s\n" << 105 | " -> writing to ‘/nix/store/94l17wjg65wpkwcm4x51pr5dlvarip6a-" << CYAN << "gcc-4.8.2" << RESET << "’\n"; 106 | 107 | std::stringstream ssout; 108 | ssout << "-----------------------------\n"; 109 | ssout << MAGENTA << "building:" << RESET << "\n"; 110 | ssout << " " << "/nix/store/ylcpwyczz887grq8lzdz8hn81q7yrn38-" << MAGENTA << "gzip-1.6" << RESET << " - 5 min " << foo << " sec" << "\n"; 111 | if (foo < 10) ssout << GREEN << "fetching:" << RESET << "\n"; 112 | if (fa < 1.0) ssout << " " << urlW0.render(); 113 | if (fb < 1.0) ssout << " " << urlW1.render(); 114 | if (fc < 1.0) ssout << " " << urlW2.render(); 115 | 116 | std::string sout = ssout.str(); 117 | std::cout << sout; 118 | int c = 0; 119 | for (int i=0 ; i <= sout.size(); ++i) 120 | if (sout[i] == '\n') 121 | c++; 122 | linecount = c; 123 | } 124 | 125 | static void pr_winsize(int fd) 126 | { 127 | struct winsize size; 128 | 129 | if (ioctl(fd, TIOCGWINSZ, (char *) &size) < 0) 130 | printf("TIOCGWINSZ error"); 131 | printf("%d rows, %d columns\n", size.ws_row, size.ws_col); 132 | } 133 | 134 | void signal_callback_handler(int signum) { 135 | printf("Caught signal %d\n",signum); 136 | 137 | //std::cout << GetEnv("PATH") << std::endl; 138 | pr_winsize(STDIN_FILENO); 139 | 140 | exit(signum); 141 | } 142 | 143 | 144 | int main() { 145 | int foo = 0; 146 | signal(SIGWINCH, signal_callback_handler); 147 | 148 | std::cout << 149 | "building Nix...\n" << 150 | "these derivations will be " << MAGENTA << "built" << RESET << ":\n" << 151 | " /nix/store/4hh3935anmmhl13q8lmdkgdsxzh46gq6-" << MAGENTA << "tig-1.1.drv\n" << RESET << 152 | " /nix/store/9brfbp9gk7x3qim52q49rckhw7vw08h2-" << MAGENTA << "asciidoc-8.6.8.drv\n" << RESET << 153 | " /nix/store/ckn6dnnb0ayjbdn3avpqvqa5rs35k2w1-" << MAGENTA << "tig-1.1.tar.gz.drv\n" << RESET << 154 | " /nix/store/j9bgfp4q6h8gdi4b6idvi1r39b5hagvz-" << MAGENTA << "asciidoc-8.6.8.tar.gz.drv\n" << RESET << 155 | "these paths will be " << GREEN << "fetched" << RESET << " (40.1 Mib download, 201.66 Mib unpacked):\n" << RESET << 156 | " /nix/store/0yzz6p08k1sgpdb63c0wx48vx0yc51g6-" << GREEN << "bzip2-1.0.6\n" << RESET << 157 | " /nix/store/1a08qk5q5vdfv13rwasbf4fqa2s26kx4-" << GREEN << "attr-2.4.47\n" << RESET << 158 | " /nix/store/3amm865b2qb5s5mwvshvd9kpfq3aj1bc-" << GREEN << "libssh2-1.4.3\n" << RESET << 159 | " /nix/store/5myfmphlck9gcabr6czlg6792d9zhh4m-" << GREEN << "perl-DBI-1.630\n" << RESET << 160 | " /nix/store/j298bijkgdzzv6wlzdidldx297ch5rq2-" << GREEN << "nix-1.7pre3327_0e2ca\n" << RESET << 161 | " /nix/store/mad928szz57gjpbfm9dih23hpspzz11f-" << GREEN << "openssl-1.0.1f\n" << RESET << 162 | " /nix/store/q784x64hp3nwdxx7lbgb16f74i2bhxxk-" << GREEN << "glibc-2.18\n" << RESET << 163 | " /nix/store/qw7vn33jcv1yfsfdw19ic5r2jlqk68w3-" << GREEN << "bash-4.2-p45\n" << RESET << 164 | " /nix/store/skxdffb34mcz50f9q691qsg44bgrxg2x-" << GREEN << "perl-DBD-SQLite-1.37\n" << RESET << 165 | " /nix/store/vmq6nmnvyblnwlrmhhhpnsjdlri4qz25-" << GREEN << "curl-7.33.0\n" << RESET << 166 | " /nix/store/xay7d5hfhm9vj3v31dbzimi08ydrgd4w-" << GREEN << "zlib-1.2.8\n" << RESET << 167 | " /nix/store/xkhr68z09y66c1qwzdq03lnhjl9c51si-" << GREEN << "perl-WWW-Curl-4.15\n" << RESET << 168 | " /nix/store/ylcpwyczz887grq8lzdz8hn81q7yrn38-" << GREEN << "gzip-1.6\n" << RESET << 169 | " /nix/store/z1krxp2hwph8fypchf2b0ssnyp6z8k9l-" << GREEN << "perl-5.16.3\n" << RESET << 170 | " /nix/store/zp4bcz188h69jvrb1qyl10lfkanz7ld3-" << GREEN << "boehm-gc-7.2d\n" << RESET << 171 | " /nix/store/zsl05mbb69s38bbyi9nfff6vyry9m8jm-" << GREEN << "gnutar-1.27.1\n" << RESET << 172 | " /nix/store/zxvyl58mw530xf811nmm0i8b6nibwmw5-" << GREEN << "coreutils-8.21\n" << RESET << 173 | "-----------------------------\n"; 174 | 175 | while(1) { 176 | drawStatus(foo); 177 | if(foo==10) { 178 | // clearStatus(); 179 | exit(0); 180 | } 181 | foo+=1; 182 | sleep(1); 183 | } 184 | } 185 | 186 | -------------------------------------------------------------------------------- /01-curl-transport.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | =head1 Curl::Transport 3 | 4 | This module shows: 5 | 6 | =over 7 | 8 | =item buildtime version check 9 | 10 | Required features will be missing if libcurl was too old at Net::Curl 11 | compilation. 12 | 13 | =item basic inheritance 14 | 15 | Use Net::Curl::* as base for your modules. 16 | 17 | =item exception handling 18 | 19 | Most methods die() with a dualvar exception on error. You can compare them 20 | numerically, or display as part of a message. 21 | 22 | =back 23 | 24 | =head2 Motivation 25 | 26 | recv() and send() methods use non-blocking transfer, this may be very annoying 27 | in simple scripts. This wrapper implements blocking send() wrapper, and two 28 | recv() wrappers called read() and readline(). 29 | 30 | =head2 MODULE CODE 31 | 32 | =cut 33 | package Curl::Transport; 34 | 35 | use strict; 36 | use warnings; 37 | use Net::Curl::Easy qw(/^CURLE_/); 38 | use base qw(Net::Curl::Easy); 39 | 40 | 41 | BEGIN { 42 | if ( Net::Curl::LIBCURL_VERSION_NUM() < 0x071202 ) { 43 | my $ver = Net::Curl::LIBCURL_VERSION(); 44 | die "curl $ver does not support send() and recv()"; 45 | } 46 | # alternatively you can write: 47 | if ( not Net::Curl::Easy->can( "send" ) 48 | or not Net::Curl::Easy->can( "recv" ) ) { 49 | die "Net::Curl is missing send() and recv()\n" 50 | } 51 | } 52 | 53 | use constant { 54 | B_URI => 0, 55 | B_SOCKET => 1, 56 | B_VEC => 2, 57 | B_READBUF => 3, 58 | }; 59 | 60 | sub cb_progress { 61 | my ( $easy, $dltotal, $dlnow, $ultotal, $ulnow, $uservar ) = @_; 62 | print "================== download status beg ==================\n"; 63 | print $dltotal; 64 | print $dlnow; 65 | print $ultotal; 66 | print $ulnow; 67 | print $uservar; 68 | print "================== download status end ==================\n"; 69 | return 0; 70 | } 71 | 72 | 73 | 74 | # new( URL ) -- get new object 75 | sub new 76 | { 77 | my $class = shift; 78 | my $uri = shift; 79 | 80 | # use an array as our object base 81 | my $base = [ $uri, undef, undef, '' ]; 82 | 83 | my $self = $class->SUPER::new( $base ); 84 | 85 | $self->setopt( Net::Curl::Easy::CURLOPT_URL, $uri ); 86 | $self->setopt( Net::Curl::Easy::CURLOPT_CONNECT_ONLY, 1); 87 | $self->setopt( Net::Curl::Easy::CURLOPT_NOPROGRESS, 0); 88 | $self->setopt( Net::Curl::Easy::CURLOPT_PROGRESSFUNCTION, \&cb_progress ); 89 | 90 | # will die if fails 91 | $self->perform(); 92 | 93 | $self->[ B_SOCKET ] = $self->getinfo( 94 | Net::Curl::Easy::CURLINFO_LASTSOCKET 95 | ); 96 | 97 | # prepare select vector 98 | my $vec = ''; 99 | vec( $vec, $self->[ B_SOCKET ], 1 ) = 1; 100 | $self->[ B_VEC ] = $vec; 101 | 102 | return $self; 103 | } 104 | 105 | # send( DATA ) -- send some data, wait for socket availability 106 | # if it cannot be sent all at once 107 | sub send($$) 108 | { 109 | my $self = shift; 110 | my $data = shift; 111 | 112 | while ( length $data ) { 113 | # copy, because select overwrites those values 114 | my $w = $self->[ B_VEC ]; 115 | 116 | # wait for write 117 | select undef, $w, undef, 0; 118 | 119 | # make sure some write bit is set 120 | next unless vec( $w, $self->[ B_SOCKET ], 1 ); 121 | 122 | # actually send the data 123 | my $sent = $self->SUPER::send( $data ); 124 | 125 | # remove from buffer what we sent 126 | substr $data, 0, $sent, ''; 127 | }; 128 | } 129 | 130 | # read( SIZE ) -- read SIZE bytes, wait for more data if there 131 | # wasn't enough 132 | sub read($$) 133 | { 134 | my $self = shift; 135 | my $size = shift; 136 | 137 | return '' unless $size > 0; 138 | 139 | while ( length $self->[ B_READBUF ] < $size ) { 140 | my $r = $self->[ B_VEC ]; 141 | 142 | # wait for data 143 | select $r, undef, undef, 0; 144 | 145 | # make sure some read bit is set 146 | redo unless vec( $r, $self->[ B_SOCKET ], 1 ); 147 | 148 | eval { 149 | my $l = $self->SUPER::recv( $self->[ B_READBUF ], 150 | $size - length $self->[ B_READBUF ] ); 151 | }; 152 | if ( $@ ) { 153 | if ( $@ == CURLE_UNSUPPORTED_PROTOCOL ) { 154 | my $uri = $self->[ B_URI ]; 155 | warn "Connection to $uri closed: $@\n"; 156 | last; 157 | } elsif ( $@ == CURLE_AGAIN ) { 158 | warn "nothing to read, this should not happen"; 159 | } else { 160 | die $@; 161 | } 162 | } 163 | } 164 | 165 | return substr $self->[ B_READBUF ], 0, $size, ''; 166 | } 167 | 168 | # readline() -- read until $/ 169 | sub readline($) 170 | { 171 | my $self = shift; 172 | 173 | # we allow changing $/, but we don't support $/ = undef. 174 | local $/; 175 | $/ = "\n" unless defined $/; 176 | 177 | my $idx; 178 | until ( ( $idx = index $self->[ B_READBUF ], $/ ) >= 0 ) { 179 | my $r = $self->[ B_VEC ]; 180 | 181 | # wait for data 182 | select $r, undef, undef, 0; 183 | 184 | # make sure some read bit is set 185 | next unless vec( $r, $self->[ B_SOCKET ], 1 ); 186 | 187 | # read 256 bytes, should be enough in most cases 188 | eval { 189 | $self->SUPER::recv( $self->[ B_READBUF ], 256 ); 190 | }; 191 | if ( $@ ) { 192 | if ( $@ == CURLE_UNSUPPORTED_PROTOCOL ) { 193 | my $uri = $self->[ B_URI ]; 194 | warn "Connection to $uri closed: $@\n"; 195 | last; 196 | } elsif ( $@ == CURLE_AGAIN ) { 197 | warn "nothing to read, this should not happen"; 198 | } else { 199 | die $@; 200 | } 201 | } 202 | } 203 | 204 | return substr $self->[ B_READBUF ], 0, ($idx + length $/), ''; 205 | } 206 | 207 | 1; 208 | 209 | =head2 TEST APPLICATION 210 | 211 | Sample application using this module could look like this: 212 | 213 | #!perl 214 | use strict; 215 | use warnings; 216 | use Curl::Transport; 217 | #nopod 218 | =cut 219 | package main; 220 | 221 | use strict; 222 | use warnings; 223 | #endnopod 224 | 225 | my $host = shift @ARGV || "lastlog.de"; 226 | my $request = "/misc/bigFile.bin.bz2"; 227 | #my $request = "/index.php";#"/misc/bigFile.bin.bz2"; 228 | 229 | my $t = Curl::Transport->new( "http://$host" ); 230 | $t->send( "GET $request HTTP/1.0\r\n" ); 231 | $t->send( "User-Agent: Curl::Transport test\r\n" ); 232 | $t->send( "Accept: */*\r\n" ); 233 | $t->send( "Host: $host\r\n" ); 234 | $t->send( "Connection: Close\r\n" ); 235 | $t->send( "\r\n" ); 236 | 237 | my $length; 238 | { 239 | local $/ = "\r\n"; 240 | local $_; 241 | do { 242 | $_ = $t->readline(); 243 | $length = 0 | $1 if /Content-Length:\s*(\d+)/; 244 | chomp; 245 | print "HEADER: $_\n"; 246 | } while ( length $_ ); 247 | } 248 | 249 | if ( defined $length ) { 250 | print "CCCCCCCCC Reading $length bytes of data:\n"; 251 | print $t->read( $length ); 252 | 253 | print "\nBBBBBBBBBBBB Trying to read one more byte, should fail:\n"; 254 | print $t->read( 1 ); 255 | print "\n"; 256 | } else { 257 | print "AAAAAAAA Don't know how much to read\n"; 258 | while ( $_ = $t->readline() ) { 259 | print; 260 | } 261 | } 262 | 263 | printf "Last error: %s\n", $t->error(); 264 | #nopod 265 | # vim: ts=4:sw=4 266 | -------------------------------------------------------------------------------- /nix-build-view/AdvancedString.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ADVANCEDSTRING_HPP 2 | #define ADVANCEDSTRING_HPP 3 | 4 | #include "ncurses.h" 5 | #include "ColorCodes.h" 6 | #include 7 | 8 | /* 9 | * To get an idea what this is all about have a look at the unit test which 10 | * illustrates the concepts with lots of examples 11 | */ 12 | class AdvancedString { 13 | public: 14 | 15 | AdvancedString() {} 16 | 17 | AdvancedString(std::string string, 18 | int color = COLOR_WHITE, 19 | int attributes = 0, 20 | int bgColor = COLOR_BLACK) 21 | { 22 | m_str = string; 23 | m_fontColor = color; 24 | m_attributes = attributes; 25 | m_bgColor = bgColor; 26 | } 27 | 28 | int size() const { 29 | return m_str.size(); 30 | } 31 | 32 | std::string str() const { 33 | return m_str; 34 | } 35 | 36 | int fontColor() const { 37 | return m_fontColor; 38 | } 39 | 40 | int bgColor() const { 41 | return m_bgColor; 42 | } 43 | 44 | int attributes() const { 45 | return m_attributes; 46 | } 47 | 48 | // used to render a AdvancedString to a xterm or similar (not using 49 | // ncurses painting) 50 | std::string terminalString() { 51 | std::stringstream ss; 52 | std::string color; 53 | 54 | switch (m_fontColor) { 55 | case COLOR_BLACK: 56 | color = "\033[30m"; 57 | break; 58 | 59 | case COLOR_RED: 60 | color = "\033[31m"; 61 | break; 62 | 63 | case COLOR_GREEN: 64 | color = "\033[32m"; 65 | break; 66 | 67 | case COLOR_YELLOW: 68 | color = "\033[33m"; 69 | break; 70 | 71 | case COLOR_BLUE: 72 | color = "\033[34m"; 73 | break; 74 | 75 | case COLOR_MAGENTA: 76 | color = "\033[35m"; 77 | break; 78 | 79 | case COLOR_CYAN: 80 | color = "\033[36m"; 81 | break; 82 | 83 | case COLOR_WHITE: 84 | color = "\033[37m"; 85 | break; 86 | } 87 | 88 | ss << color << m_str << RESET; 89 | return ss.str(); 90 | } 91 | 92 | static AdvancedString substr(const AdvancedString &in, 93 | unsigned int b, 94 | unsigned int len) { 95 | if (b >= in.size()) { 96 | return AdvancedString(); 97 | } 98 | 99 | return AdvancedString(in.str().substr(b, len), 100 | in.fontColor(), 101 | in.attributes(), 102 | in.bgColor()); 103 | } 104 | 105 | private: 106 | 107 | std::string m_str; 108 | int m_fontColor; 109 | int m_bgColor; 110 | int m_attributes; 111 | 112 | }; 113 | 114 | /* 115 | * main motivation for AdvancedStringContainer: 116 | * - each Widget should only return a sequence of chars which are then drawn by 117 | * the WindowManager 118 | * - this prevents that every widget has full draw support to the whole screen 119 | * - however, still every widget can use all the formating extensions like 120 | * color, bold and italic text 121 | * 122 | * my requirements were: 123 | * - i need to be able to count the amount of chars (without color escape 124 | * sequences) to predict if the string fits into the given widget's width 125 | * - to print the string to a normal xterm with colors while 126 | * - also using the same representation to draw it using ncurses 127 | */ 128 | class AdvancedStringContainer { 129 | public: 130 | 131 | AdvancedStringContainer& operator<< (const AdvancedString& t) { 132 | sContainer.push_back(t); 133 | return *this; 134 | } 135 | 136 | AdvancedStringContainer& operator<< (const AdvancedStringContainer& c) { 137 | for (unsigned int i = 0; i < c.size(); ++i) { 138 | sContainer.push_back(c[i]); 139 | } 140 | 141 | return *this; 142 | } 143 | 144 | AdvancedStringContainer& operator<< (const std::string& s1) { 145 | sContainer.push_back(AdvancedString(s1)); 146 | return *this; 147 | } 148 | 149 | AdvancedStringContainer& operator<< (const int& t) { 150 | sContainer.push_back(AdvancedString(std::to_string(t))); 151 | return *this; 152 | } 153 | 154 | AdvancedStringContainer& operator<< (const size_t& t) { 155 | sContainer.push_back(AdvancedString(std::to_string(t))); 156 | return *this; 157 | } 158 | 159 | AdvancedStringContainer& operator<< (const unsigned int& t) { 160 | sContainer.push_back(AdvancedString(std::to_string(t))); 161 | return *this; 162 | } 163 | 164 | AdvancedStringContainer& operator<< (const float& f) { 165 | sContainer.push_back(AdvancedString(std::to_string(f))); 166 | return *this; 167 | } 168 | 169 | AdvancedString operator[] (unsigned int element) const { 170 | if (element < sContainer.size()) { 171 | return sContainer[element]; 172 | } else { 173 | return AdvancedString(); 174 | } 175 | } 176 | 177 | unsigned int size() const { 178 | return sContainer.size(); 179 | } 180 | 181 | int str_size() const { 182 | unsigned int size = 0; 183 | for (unsigned int i = 0; i < sContainer.size(); ++i) { 184 | size += sContainer[i].size(); 185 | } 186 | return size; 187 | } 188 | 189 | std::string str() { 190 | std::string s; 191 | 192 | for (unsigned int i = 0; i < sContainer.size(); ++i) { 193 | s += sContainer[i].str(); 194 | } 195 | 196 | return s; 197 | } 198 | 199 | void clear() { 200 | sContainer.clear(); 201 | } 202 | 203 | /* 204 | * like std::string.substr this function builds a substring starting at b 205 | * and ending at b+len 206 | * - if b > in.str_size(), an empty AdvancedStringContainer is returned 207 | * 208 | * note: it is assumed that the string is free of \n, \t chars or terminal 209 | * control sequences of any kind 210 | * if not, they are treated as normal chars 211 | * note: empty AdvancedString's will be removed 212 | */ 213 | static void substr(AdvancedStringContainer &out, 214 | const AdvancedStringContainer &in, 215 | unsigned int b, 216 | unsigned int len) 217 | { 218 | // if (b >= in.str_size()) 219 | // return; 220 | // bool begin_found=false; 221 | // int visited = 0; 222 | // unsigned int left = len; 223 | // for (unsigned int i=0; i < in.size(); i++) { 224 | // if (in[i].size() == 0) 225 | // continue; 226 | // if (!begin_found) { 227 | // if (visited + in[i].size() < b) { 228 | // visited += in[i].size(); 229 | // continue; 230 | // } else { 231 | // begin_found = true; 232 | // } 233 | // } else { 234 | // // 5 cases: 235 | // if (visited < b) { 236 | // int l = 0; 237 | // int x = b-visited; 238 | // if (in[i].size() >= x) 239 | // l = x; 240 | // else 241 | // l = in[i].size(); 242 | // // 1. part from the middle needed, add it and return 243 | // std::sting s = in[i].substr(x, l); 244 | // out << AdvancedString(s, in[i].fontColor(), in[i].attributes(), in[i].bgColor()); 245 | // left -= l; 246 | // // 2. first part is not needed, add rest 247 | // in[i].substr(x, string::npos) 248 | // 249 | // 250 | // } else { 251 | // // 3. fits completely (just add it), 252 | // // 4. last part is not needed, leave rest and return 253 | // 254 | // 255 | // } 256 | // // 5. no more input, return 257 | // } 258 | // } 259 | } 260 | 261 | /* 262 | * removes newline characters by transforming a AdvancedStringContainer 263 | * (a list of words) into vector 264 | * (a vector of sentences) 265 | */ 266 | static void containerStringSplit(std::vector &out, 267 | AdvancedStringContainer &in, 268 | const char ch) 269 | { 270 | AdvancedStringContainer tmp; 271 | for (unsigned int i = 0; i < in.size(); i++) { 272 | AdvancedString as = in[i]; 273 | std::string str = as.str(); 274 | std::string sub; 275 | std::string::size_type pos = 0; 276 | std::string::size_type old_pos = 0; 277 | bool flag = true; 278 | 279 | while (flag) { 280 | pos = str.find_first_of(ch,pos); 281 | 282 | if (pos == std::string::npos) { 283 | flag = false; 284 | pos = str.size(); 285 | } 286 | 287 | sub = str.substr(old_pos, pos - old_pos); 288 | 289 | tmp << AdvancedString(sub, as.fontColor(), 290 | as.attributes(), 291 | as.bgColor()); 292 | 293 | if (flag || i == in.size() - 1) { 294 | out.push_back(tmp); 295 | tmp.clear(); 296 | } 297 | 298 | old_pos = ++pos; 299 | } 300 | } 301 | } 302 | 303 | /* 304 | * trimes right side of a sentence (AdvancedStringContainer are 305 | * considered sentences) and removes newline characters using 306 | * containerStringSplit 307 | */ 308 | static void trimEndAndRemoveNewlineChars( 309 | std::vector &out, 310 | AdvancedStringContainer &in) 311 | { 312 | std::vector tmp; 313 | AdvancedStringContainer tmp2; 314 | AdvancedStringContainer asc_tmp; 315 | 316 | containerStringSplit(tmp, in, '\n'); 317 | 318 | bool run = true; 319 | 320 | // process vector of sentences (tmp) 321 | for (unsigned int i = 0; i < tmp.size(); ++i) { 322 | AdvancedStringContainer asc = tmp[i]; 323 | 324 | if (asc.str_size()) { 325 | // process all words, inside a single sentence, in reverse and 326 | // remove traling white spaces (including words built of 327 | // white spaces) 328 | for (int x = asc.size() - 1; x >= 0; x--) { 329 | AdvancedString as = asc[x]; 330 | 331 | if (as.size()) { 332 | // process a single word in reverse and find all 333 | // whitespaces 334 | std::string s = as.str(); 335 | 336 | for (int y = as.size() - 1; y >= 0; y--) { 337 | if (s[y] != ' ') { 338 | std::string sub = s.substr(0, y + 1); 339 | AdvancedString n = AdvancedString(sub, 340 | as.fontColor(), 341 | as.attributes(), 342 | as.bgColor()); 343 | for (int t = 0; t < x; ++t) { 344 | asc_tmp << asc[t]; 345 | } 346 | 347 | asc_tmp << n; 348 | out.push_back(asc_tmp); 349 | run = false; 350 | asc_tmp.clear(); 351 | break; 352 | } 353 | } 354 | 355 | if (!run) { 356 | run = true; 357 | break; 358 | } 359 | } 360 | 361 | if (x == 0) { 362 | AdvancedString n = AdvancedString("", 363 | COLOR_YELLOW, 364 | as.attributes(), 365 | as.bgColor()); 366 | asc_tmp << n; 367 | out.push_back(asc_tmp); 368 | asc_tmp.clear(); 369 | } 370 | } 371 | } else { 372 | AdvancedString n = AdvancedString("", COLOR_YELLOW); 373 | asc_tmp << n; 374 | out.push_back(asc_tmp); 375 | asc_tmp.clear(); 376 | } 377 | } 378 | } 379 | 380 | /* 381 | * translates AdvancedStringContainer (a list of words) into a fixed width 382 | * vector of AdvancedStringContainer for later displaying 383 | * - each vector element holds exactly width chars 384 | * - if the input doesn't fill the whole line it is padded with white spaces 385 | * 386 | * this function is used by the Widget::render() call to compute the 387 | * width x height chars needed for filling the widget space on the terminal 388 | */ 389 | static void terminal_rasterize(std::vector &out, 390 | AdvancedStringContainer &in, 391 | int width) 392 | { 393 | // - render the text to a buffer4 394 | // - do line-wrapping 395 | std::vector buf; 396 | 397 | // trims trailing newlines and writes result to buf 398 | trimEndAndRemoveNewlineChars(buf, in); 399 | 400 | // render the m_logfile into a terminal with width() 401 | out.clear(); 402 | 403 | AdvancedStringContainer tmp; 404 | // process vector of sentences (buf) 405 | for (unsigned int i = 0; i < buf.size(); ++i) { 406 | AdvancedStringContainer asc = buf[i]; 407 | 408 | // process all words, inside a single sentence, 409 | for (unsigned int x = 0; x < asc.size(); x++) { 410 | AdvancedString as = asc[x]; 411 | AdvancedString atmp; 412 | 413 | // std::string::iterator it_begin = as.str().begin(); 414 | // std::string::iterator it_p = as.str().begin(); 415 | // std::string::iterator it_e = as.str().end(); 416 | 417 | if ((as.size() == 0) && 418 | (x == asc.size() - 1) && 419 | (i != buf.size() - 1)) 420 | { 421 | tmp << AdvancedString(std::string(width, ' '), COLOR_BLUE); 422 | out.push_back(tmp); 423 | tmp.clear(); 424 | } 425 | 426 | while (as.size()) { 427 | int size = 0; 428 | 429 | if (as.size() > width-tmp.str_size()) { 430 | // if string is bigger than available size 431 | size = width-tmp.str_size(); 432 | } else { 433 | size = as.size(); 434 | } 435 | 436 | std::string sub1 = as.str().substr(0, size); 437 | atmp = AdvancedString(sub1, 438 | as.fontColor(), 439 | as.attributes(), 440 | as.bgColor()); 441 | 442 | std::string sub2 = as.str().substr(size, as.size() - size); 443 | as = AdvancedString(sub2, 444 | as.fontColor(), 445 | as.attributes(), 446 | as.bgColor()); 447 | tmp << atmp; 448 | 449 | // check if padding is needed 450 | if ((width - tmp.str_size() == 0) || 451 | (x == asc.size() - 1)) 452 | { 453 | int f = width - tmp.str_size(); 454 | 455 | if (f > 0) { 456 | tmp << AdvancedString(std::string(f, ' ')); 457 | } 458 | 459 | out.push_back(tmp); 460 | tmp.clear(); 461 | } 462 | } 463 | } 464 | } 465 | } 466 | 467 | private: 468 | 469 | std::vector sContainer; 470 | 471 | }; 472 | 473 | #endif // ADVANCEDSTRING_HPP 474 | -------------------------------------------------------------------------------- /design-experiments/demo2.cpp: -------------------------------------------------------------------------------- 1 | // http://infohost.nmt.edu/~eweiss/222_book/222_book/0201433079/ch18lev1sec12.html 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include /* printf */ 12 | #include /* signal, raise, sig_atomic_t */ 13 | 14 | #include 15 | #include 16 | #ifndef TIOCGWINSZ 17 | #include 18 | #endif 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | using namespace std; 26 | 27 | // http://ispltd.org/mini_howto:ansi_terminal_codes 28 | #define CURSOR_SAVE "\e7" 29 | #define CURSOR_RESTORE "\e8" 30 | #define CURSOR_TO_START_OF_LINE "\r" 31 | #define CURSOR_UP_ONE_LINE "\e[A" 32 | #define CURSOR_CLEAR_TO_EOL "\e[K" 33 | #define CURSOR_CLEAN_ALL_AFTERWARDS "\e[J" 34 | 35 | #define RESET "\033[0m" 36 | #define BLACK "\033[30m" /* Black */ 37 | #define RED "\033[31m" /* Red */ 38 | #define GREEN "\033[32m" /* Green */ 39 | #define YELLOW "\033[33m" /* Yellow */ 40 | #define BLUE "\033[34m" /* Blue */ 41 | #define MAGENTA "\033[35m" /* Magenta */ 42 | #define CYAN "\033[36m" /* Cyan */ 43 | #define WHITE "\033[37m" /* White */ 44 | #define BOLDBLACK "\033[1m\033[30m" /* Bold Black */ 45 | #define BOLDRED "\033[1m\033[31m" /* Bold Red */ 46 | #define BOLDGREEN "\033[1m\033[32m" /* Bold Green */ 47 | #define BOLDYELLOW "\033[1m\033[33m" /* Bold Yellow */ 48 | #define BOLDBLUE "\033[1m\033[34m" /* Bold Blue */ 49 | #define BOLDMAGENTA "\033[1m\033[35m" /* Bold Magenta */ 50 | #define BOLDCYAN "\033[1m\033[36m" /* Bold Cyan */ 51 | #define BOLDWHITE "\033[1m\033[37m" /* Bold White */ 52 | 53 | sig_atomic_t signaled = 1; 54 | 55 | struct timeval cur_t; 56 | struct timeval last_t; 57 | struct timeval activity_timer; 58 | 59 | static int linecount = 0; 60 | static std::stringstream olds; 61 | 62 | 63 | 64 | //FIXME description needs update 65 | // motivation: std::stringstream must be wrapped since terminal control chars might be arbitrary and to 66 | // count stringlength they have to be ignored. this is needed for rendering the string to the shell. 67 | // TermCtrl wraps std::string and color=0 means no color; color>0 will be replaced by the colortable entry 68 | class AdvancedString { 69 | public: 70 | AdvancedString(std::string str, bool isTermCtrl) { 71 | m_str = str; 72 | m_isTermCtrl = isTermCtrl; 73 | } 74 | AdvancedString(std::string str) { 75 | m_str = str; 76 | m_isTermCtrl = true; 77 | } 78 | // mode==0 -> BW, mode==1 -> color 79 | std::string render(int mode) { 80 | if (mode == 0) // BW rendering 81 | if (m_isTermCtrl == true) { 82 | return ""; 83 | } 84 | return m_str; 85 | } 86 | private: 87 | std::string m_str; 88 | bool m_isTermCtrl; 89 | }; 90 | 91 | // AdvancedStringList can render to std:string with or without using terminal color codes 92 | class AdvancedStringList { 93 | public: 94 | //FIXME check the return *this... is this good code? 95 | AdvancedStringList& operator<<( const std::string& s ) { 96 | cStrings.push_back(AdvancedString(s, false)); 97 | return *this; 98 | } 99 | AdvancedStringList& operator<<( const AdvancedString& t ) { 100 | cStrings.push_back(t); 101 | return *this; 102 | } 103 | AdvancedStringList& operator<<( const int& t ) { 104 | cStrings.push_back(AdvancedString(std::to_string(t), false)); 105 | return *this; 106 | } 107 | std::string str() { 108 | return render_color(0); 109 | } 110 | std::string color_str() { 111 | return render_color(1); 112 | } 113 | private: 114 | std::string render_color(int color) { 115 | std::stringstream sStream; 116 | for(int i=0; i < cStrings.size(); ++i) { 117 | sStream << cStrings[i].render(color); 118 | } 119 | return sStream.str(); 120 | } 121 | std::vector cStrings; 122 | }; 123 | 124 | class Widget { 125 | public: 126 | std::string render(int width=0); 127 | }; 128 | 129 | class FetchWidget : public Widget { 130 | private: 131 | std::string m_name; 132 | float m_percent; 133 | int m_bits_per_sec; 134 | public: 135 | FetchWidget(std::string url, float percent, int bits_per_sec) { 136 | m_name=url; 137 | m_percent = percent; 138 | m_bits_per_sec = bits_per_sec; 139 | }; 140 | //FIXME this code needs AdvancedStringList rewrite! 141 | std::string render(int width=0) { 142 | std::stringstream s; 143 | 144 | int size = m_name.size(); 145 | std::stringstream url_progress; 146 | float end = m_percent * size; 147 | url_progress << CYAN << m_name.substr(0, (int)end) << RESET << m_name.substr((int)end, size-(int)end); 148 | 149 | s << url_progress.str() << " " << (int)(m_percent*100) << "% " << m_bits_per_sec << "\n"; 150 | olds << std::flush; 151 | olds << s; 152 | return s.str(); 153 | } 154 | }; 155 | 156 | void clearStatus() { 157 | // printf("\e[%iA", linecount); 158 | 159 | // printf("\e[r"); 160 | for (int i=0; i < linecount; i++) { 161 | // printf("\e[1F"); 162 | // printf(CURSOR_TO_START_OF_LINE); 163 | 164 | printf(CURSOR_UP_ONE_LINE); 165 | // printf(CURSOR_CLEAR_TO_EOL); 166 | } 167 | printf(CURSOR_CLEAN_ALL_AFTERWARDS); 168 | } 169 | 170 | 171 | void drawStatus(int foo) { 172 | float fa = (((float)foo/5)*0.8) < 1.0 ? ((float)foo/5)*0.8 : 1.0; 173 | float fb = (((float)foo/5)*0.5) < 1.0 ? ((float)foo/5)*0.5 : 1.0; 174 | float fc = (((float)foo/5)*1.7) < 1.0 ? ((float)foo/5)*1.7 : 1.0; 175 | FetchWidget urlW0("http://cache.nixos.org/nar/0s57kyi85g7lb9irja2cslmh5vc23i4q35dv8pi4gh1-foobar-1.2.3.nar.xz", fa, 12356); 176 | FetchWidget urlW1("http://cache.nixos.org/nar/0s57kyi85g7lb9irja3i4q35dv8pi4gh1-foobar-1.2.3.nar.xz .........", fb, 12356); 177 | FetchWidget urlW2("http://cache.nixos.org/nar/0s57kyi8-foobar-1.2.3.nar.xz ..................................", fc, 12356); 178 | 179 | clearStatus(); 180 | 181 | //FIXME this must not be done here! or it will be written a gazillion times on terminal resize! 182 | // if (foo == 3 || foo == 7 || foo == 10) 183 | // std::cout << " Download of " << CYAN << "http://cache.nixos.org/nar/00fwcb3janb72b1xf4rnq7ninzmvm8zzzlr6lc8sp9dbl7x838iz.nar.xz" << RESET << " finished\n" << 184 | // " -> 24.4 Mib in 0:01:25, average speed 115 kib/s\n" << 185 | // " -> writing to ‘/nix/store/94l17wjg65wpkwcm4x51pr5dlvarip6a-" << CYAN << "gcc-4.8.2" << RESET << " - " << foo << "’\n"; 186 | 187 | AdvancedStringList aout; 188 | 189 | //FIXME found new bug: when amount of lines written exceeds the number of visibile lines it removes a false amount of lines and damages the terminal output 190 | aout << "-----------------------------\n"; 191 | aout << AdvancedString(MAGENTA) << "building:" << AdvancedString(RESET) << "\n"; 192 | aout << AdvancedString(MAGENTA) << "building:" << AdvancedString(RESET) << "\n"; 193 | aout << AdvancedString(MAGENTA) << "building:" << AdvancedString(RESET) << "\n"; 194 | aout << AdvancedString(MAGENTA) << "building:" << AdvancedString(RESET) << "\n"; 195 | aout << AdvancedString(MAGENTA) << "building:" << AdvancedString(RESET) << "\n"; 196 | aout << AdvancedString(MAGENTA) << "building:" << AdvancedString(RESET) << "\n"; 197 | 198 | aout << AdvancedString(MAGENTA) << "building:" << AdvancedString(RESET) << "\n"; 199 | aout << AdvancedString(MAGENTA) << "building:" << AdvancedString(RESET) << "\n"; 200 | aout << AdvancedString(MAGENTA) << "building:" << AdvancedString(RESET) << "\n"; 201 | 202 | aout << AdvancedString(MAGENTA) << "building:" << AdvancedString(RESET) << "\n"; 203 | aout << AdvancedString(MAGENTA) << "building:" << AdvancedString(RESET) << "\n"; 204 | aout << AdvancedString(MAGENTA) << "building:" << AdvancedString(RESET) << "\n"; 205 | 206 | aout << AdvancedString(MAGENTA) << "building:" << AdvancedString(RESET) << "\n"; 207 | 208 | // aout << TermCtrl(MAGENTA) << "building:" << TermCtrl(RESET) << "\n"; 209 | // aout << TermCtrl(MAGENTA) << "building:" << TermCtrl(RESET) << "\n"; 210 | // aout << TermCtrl(MAGENTA) << "building:" << TermCtrl(RESET) << "\n"; 211 | // aout << TermCtrl(MAGENTA) << "building:" << TermCtrl(RESET) << "\n"; 212 | // aout << TermCtrl(MAGENTA) << "building:" << TermCtrl(RESET) << "\n"; 213 | // aout << TermCtrl(MAGENTA) << "building:" << TermCtrl(RESET) << "\n"; 214 | // 215 | // aout << TermCtrl(MAGENTA) << "building:" << TermCtrl(RESET) << "\n"; 216 | // aout << TermCtrl(MAGENTA) << "building:" << TermCtrl(RESET) << "\n"; 217 | // aout << TermCtrl(MAGENTA) << "building:" << TermCtrl(RESET) << "\n"; 218 | // 219 | // aout << TermCtrl(MAGENTA) << "building:" << TermCtrl(RESET) << "\n"; 220 | // aout << TermCtrl(MAGENTA) << "building:" << TermCtrl(RESET) << "\n"; 221 | // aout << TermCtrl(MAGENTA) << "building:" << TermCtrl(RESET) << "\n"; 222 | 223 | aout << AdvancedString(MAGENTA) << "building:" << AdvancedString(RESET) << "\n"; 224 | aout << AdvancedString(MAGENTA) << "building:" << AdvancedString(RESET) << "\n"; 225 | aout << AdvancedString(MAGENTA) << "building:" << AdvancedString(RESET) << "\n"; 226 | aout << AdvancedString(MAGENTA) << "building:" << AdvancedString(RESET) << "\n"; 227 | //FIXME long strings will create a problem when being wrapped.... duno where the error is, need to investegate! 228 | aout << AdvancedString(MAGENTA) << "buildingdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddbuildingdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddbuildingdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddbuildingdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddbuildingdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddbuildingddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd dddd:" << AdvancedString(RESET) << "\n"; 229 | aout << AdvancedString(MAGENTA) << "building:" << AdvancedString(RESET) << "\n"; 230 | // aout << " " << "/nix/store/ylcpwyczz887grq8lzdz8hn81q7yrn38-" << TermCtrl(MAGENTA) << "gzip-1.6" << TermCtrl(RESET) << " - 2m" << foo+28 << "s " << "\n"; 231 | // aout << " [ " << TermCtrl(BOLDYELLOW) << "installationPhase" << TermCtrl(RESET) << " ] - /tmp/build-ylcpwyczz887grq8lzdz8hn81q7yrn38/log\n"; 232 | // aout << " " << "/nix/store/ylcpwyczz887grq8lzdz8hn81q7yrn38-" << TermCtrl(MAGENTA) << "foobar-1.7" << TermCtrl(RESET) << " - 5m" << foo+17 << "s " << "\n"; 233 | // aout << " [ " << TermCtrl(BOLDYELLOW) << "postinstallationPhase" << TermCtrl(RESET) << " ] - /tmp/build-yczz887grq8lzdylcpwz8hn81q7yrn38/log\n"; 234 | if (foo < 10) aout << AdvancedString(GREEN) << "fetching:" << AdvancedString(RESET) << "\n"; 235 | //FIXME the UrlWidget code is not using TermCtrl and AdvancedStringList yet so fix that! 236 | // if (fa < 1.0) aout << " " << urlW0.render(); 237 | // if (fb < 1.0) aout << " " << urlW1.render(); 238 | // if (fc < 1.0) aout << " " << urlW2.render(); 239 | aout << "=== " << AdvancedString(YELLOW) << "running since 20m" << foo+12 << "s" << AdvancedString(RESET) << " ===" << "\n"; 240 | 241 | struct winsize size; 242 | if (ioctl(0, TIOCGWINSZ, (char *) &size) < 0) 243 | printf("TIOCGWINSZ error"); 244 | 245 | std::cout << aout.color_str(); 246 | 247 | /////////////////////////////////////////////// 248 | // fprintf(stderr, "=====================================\n"); 249 | 250 | std::vector strings; 251 | std::string s; 252 | linecount=0; 253 | std::stringstream ssout; 254 | ssout << aout.str(); // to compute the real width, we need the strings without terminal control sequences 255 | while(std::getline(ssout, s, '\n')) { 256 | strings.push_back(s); 257 | } 258 | // fprintf(stderr, "strings.size()=%i\n", strings.size()); 259 | for(int i=0; i < strings.size(); ++i) { 260 | int v = strings[i].size()/size.ws_col; 261 | int r = strings[i].size()%size.ws_col; 262 | if (v > 0 && r == 0) 263 | v--; 264 | linecount+=v; 265 | // fprintf(stderr, "strings[i].size=%i, i=%i, v=%i, size.ws_col=%i\n", strings[i].size(), i,v, size.ws_col); 266 | // fprintf(stderr, "%s\n", strings[i].c_str()); 267 | 268 | } 269 | linecount += strings.size(); 270 | // fprintf(stderr, "=====================================\n"); 271 | ////////////////////////////////////////////// 272 | } 273 | 274 | // std::string GetEnv( const string & var ) { 275 | // const char * val = ::getenv( var.c_str() ); 276 | // if ( val == 0 ) { 277 | // return ""; 278 | // } 279 | // else { 280 | // return val; 281 | // } 282 | // } 283 | 284 | // static void pr_winsize(int fd) 285 | // { 286 | // struct winsize size; 287 | // 288 | // if (ioctl(fd, TIOCGWINSZ, (char *) &size) < 0) 289 | // printf("TIOCGWINSZ error"); 290 | // printf("%d rows, %d columns\n", size.ws_row, size.ws_col); 291 | // } 292 | 293 | void signal_callback_handler(int signum) { 294 | //printf("Caught signal %d\n",signum); 295 | 296 | //std::cout << GetEnv("PATH") << std::endl; 297 | if (signum == SIGINT) 298 | signaled = 0; 299 | } 300 | 301 | 302 | int main() { 303 | int foo = 0; 304 | struct winsize size; 305 | if (ioctl(0, TIOCGWINSZ, (char *) &size) < 0) 306 | printf("TIOCGWINSZ error"); 307 | //printf("%d rows, %d columns\n", size.ws_row, size.ws_col); 308 | 309 | 310 | /////////////////////////////////////////////////////////////// 311 | // AdvancedStringList ad; 312 | // ad << TermCtrl(RED, "hello world\n"); 313 | // ad << "hello"; 314 | // ad << " world\n"; 315 | // ad << TermCtrl(MAGENTA, "i love you\n"); 316 | // std::cout << ad.color_str() << std::endl; 317 | // exit(0); 318 | /////////////////////////////////////////////////////////////// 319 | 320 | 321 | // another test BEGIN 322 | // this test prooved what i had suspeced, that is: when doing printf to the terminal it will compute line-wrap once 323 | // but never afterwards. this means that if one writes 3 lines and the console is resized afterwards, it will 324 | // always be 3 lines needed to be removed. not more not less! 325 | // 326 | // 1. execute this program until you can see the 11111____* string on the shell, then quick 327 | // 2. resize the terminal to 30 cols and afterwards the right amount of cols will be removed 328 | // 3. also test vice versa, start at 30 cols and then resize it to 130 cols, must have the same effect [tm] 329 | // 330 | //std::cout << "==================oben=================\n"; 331 | 332 | //std::string s = "11111_____11112_____11113_____11114_____11115_____11116_____11117_____11118_____11119_____11121_____11122_____\n"; 333 | //std::cout << s; 334 | //sleep(1); 335 | 336 | //int c=0; 337 | //for(int i=0; i < s.size(); ++i) { 338 | // if(s[i] == '\n') 339 | // c+=(i/size.ws_col)+1; 340 | //} 341 | 342 | //while(c--) 343 | // printf(CURSOR_UP_ONE_LINE); 344 | //printf(CURSOR_CLEAN_ALL_AFTERWARDS); 345 | //std::cout << "==================unten=================\n"; 346 | //pr_winsize(0); 347 | //std::cout << s.size(); 348 | 349 | //exit(1); 350 | // another test END 351 | 352 | 353 | //http://stackoverflow.com/questions/4963421/vt-terminal-disable-local-editing-and-echo 354 | struct termios term_stored; 355 | struct termios term_new; 356 | struct termios term_old; 357 | tcgetattr(0,&term_stored); 358 | tcgetattr(0,&term_old); 359 | memcpy(&term_new,&term_old,sizeof(struct termios)); 360 | term_new.c_lflag &= ~(ECHO|ICANON); /* disable echo and canonization */ 361 | tcsetattr(0,TCSANOW,&term_new); 362 | 363 | //signal(SIGWINCH, signal_callback_handler); 364 | 365 | // https://lwn.net/Articles/176911/ 366 | sigset_t emptyset, blockset; 367 | 368 | sigemptyset(&blockset); /* Block SIGINT */ 369 | sigaddset(&blockset, SIGINT); 370 | sigaddset(&blockset, SIGWINCH); 371 | sigaddset(&blockset, SIGSTOP); 372 | sigprocmask(SIG_BLOCK, &blockset, NULL); 373 | 374 | struct sigaction sa; 375 | sa.sa_handler = signal_callback_handler; /* Establish signal handler */ 376 | sa.sa_flags = 0; 377 | sigemptyset(&sa.sa_mask); 378 | sigaction(SIGINT, &sa, NULL); 379 | sigaction(SIGWINCH, &sa, NULL); 380 | //sigaction(SIGSTART, &sa, NULL); 381 | 382 | std::cout << 383 | "building Nix...\n" << 384 | "these derivations will be " << MAGENTA << "built" << RESET << ":\n" << 385 | " /nix/store/4hh3935anmmhl13q8lmdkgdsxzh46gq6-" << MAGENTA << "tig-1.1.drv\n" << RESET << 386 | " /nix/store/9brfbp9gk7x3qim52q49rckhw7vw08h2-" << MAGENTA << "asciidoc-8.6.8.drv\n" << RESET << 387 | " /nix/store/ckn6dnnb0ayjbdn3avpqvqa5rs35k2w1-" << MAGENTA << "tig-1.1.tar.gz.drv\n" << RESET << 388 | " /nix/store/j9bgfp4q6h8gdi4b6idvi1r39b5hagvz-" << MAGENTA << "asciidoc-8.6.8.tar.gz.drv\n" << RESET << 389 | "these paths will be " << GREEN << "fetched" << RESET << " (40.1 Mib download, 201.66 Mib unpacked):\n" << RESET << 390 | " /nix/store/0yzz6p08k1sgpdb63c0wx48vx0yc51g6-" << GREEN << "bzip2-1.0.6\n" << RESET << 391 | " /nix/store/1a08qk5q5vdfv13rwasbf4fqa2s26kx4-" << GREEN << "attr-2.4.47\n" << RESET << 392 | " /nix/store/3amm865b2qb5s5mwvshvd9kpfq3aj1bc-" << GREEN << "libssh2-1.4.3\n" << RESET << 393 | " /nix/store/5myfmphlck9gcabr6czlg6792d9zhh4m-" << GREEN << "perl-DBI-1.630\n" << RESET << 394 | " /nix/store/j298bijkgdzzv6wlzdidldx297ch5rq2-" << GREEN << "nix-1.7pre3327_0e2ca\n" << RESET << 395 | " /nix/store/mad928szz57gjpbfm9dih23hpspzz11f-" << GREEN << "openssl-1.0.1f\n" << RESET << 396 | " /nix/store/q784x64hp3nwdxx7lbgb16f74i2bhxxk-" << GREEN << "glibc-2.18\n" << RESET << 397 | " /nix/store/qw7vn33jcv1yfsfdw19ic5r2jlqk68w3-" << GREEN << "bash-4.2-p45\n" << RESET << 398 | " /nix/store/skxdffb34mcz50f9q691qsg44bgrxg2x-" << GREEN << "perl-DBD-SQLite-1.37\n" << RESET << 399 | " /nix/store/vmq6nmnvyblnwlrmhhhpnsjdlri4qz25-" << GREEN << "curl-7.33.0\n" << RESET << 400 | " /nix/store/xay7d5hfhm9vj3v31dbzimi08ydrgd4w-" << GREEN << "zlib-1.2.8\n" << RESET << 401 | " /nix/store/xkhr68z09y66c1qwzdq03lnhjl9c51si-" << GREEN << "perl-WWW-Curl-4.15\n" << RESET << 402 | " /nix/store/ylcpwyczz887grq8lzdz8hn81q7yrn38-" << GREEN << "gzip-1.6\n" << RESET << 403 | " /nix/store/z1krxp2hwph8fypchf2b0ssnyp6z8k9l-" << GREEN << "perl-5.16.3\n" << RESET << 404 | " /nix/store/zp4bcz188h69jvrb1qyl10lfkanz7ld3-" << GREEN << "boehm-gc-7.2d\n" << RESET << 405 | " /nix/store/zsl05mbb69s38bbyi9nfff6vyry9m8jm-" << GREEN << "gnutar-1.27.1\n" << RESET << 406 | " /nix/store/zxvyl58mw530xf811nmm0i8b6nibwmw5-" << GREEN << "coreutils-8.21\n" << RESET << 407 | "-----------------------------\n"; 408 | 409 | fd_set rfds; 410 | struct timespec tv; 411 | int retval; 412 | 413 | gettimeofday(&cur_t, 0); 414 | gettimeofday(&last_t, 0); 415 | gettimeofday(&activity_timer, 0); 416 | 417 | /* Watch skt to see when it has input. */ 418 | FD_ZERO(&rfds); 419 | //FD_SET(skt, &rfds); 420 | 421 | tv.tv_sec = 1; 422 | tv.tv_nsec = 0; 423 | 424 | while(signaled) { 425 | // have to use pselect() 426 | // todo: 427 | // - pass stdin/stderr through 428 | // - update status every second 429 | // - process redraws from the sinal handler AND from each forked and downloading|building child process by using socket writes 430 | sigemptyset(&emptyset); 431 | retval = pselect(1, &rfds, NULL, NULL, &tv, &emptyset); 432 | 433 | if (retval < 0) { 434 | if (errno == EINTR) { 435 | //printf("signal\n"); 436 | //printf("%lu, %lu\n", tv.tv_sec, tv.tv_nsec); 437 | drawStatus(foo); 438 | continue; 439 | } 440 | printf("error in pselect\n"); 441 | exit(1); 442 | } 443 | // timeout 444 | if (retval == 0) { 445 | drawStatus(foo); 446 | if(foo==10) { 447 | //clearStatus(); 448 | exit(0); 449 | } 450 | foo+=1; 451 | 452 | tv.tv_sec = 1; 453 | tv.tv_nsec = 0; 454 | 455 | // resetting this is mendatory (see manpage: man 2 select) 456 | FD_ZERO(&rfds); 457 | //FD_SET(skt, &rfds); 458 | } 459 | 460 | // process FDs since data is there 461 | // see http://publib.boulder.ibm.com/infocenter/iseries/v5r3/index.jsp?topic=%2Frzab6%2Frzab6xnonblock.htm 462 | if (retval > 0) { 463 | 464 | } 465 | } 466 | tcsetattr(0,TCSANOW,&term_stored); /* restore the original state */ 467 | } 468 | 469 | 470 | 471 | 472 | 473 | -------------------------------------------------------------------------------- /nix-build-view/TDD-AdvancedStringTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void 7 | test1(AdvancedStringContainer& a, 8 | std::vector res, 9 | int num) 10 | { 11 | std::vector t; 12 | AdvancedStringContainer::containerStringSplit(t, a, '\n'); 13 | 14 | bool r = true; 15 | 16 | if (t.size() != res.size()) { 17 | r = false; 18 | } 19 | 20 | if (r) { 21 | for (int i=0; i < t.size(); ++i) { 22 | if (t[i].str() != res[i].str()) { 23 | r = false; 24 | break; 25 | } 26 | } 27 | } 28 | 29 | if (!r) { 30 | std::cout << YELLOW << "---------------------------BEGIN TEST containerStringSplit----------------------------------------------" << RESET << std::endl; 31 | std::cout << RED << "TEST " << num << " WAS A FAILURE" << RESET << std::endl; 32 | std::cout << "result was:" << std::endl; 33 | 34 | for (int i = 0; i < t.size(); ++i) { 35 | std::cout << "'" << RED << t[i].str() << RESET << "'" << std::endl; 36 | } 37 | 38 | std::cout << "but should be:" << std::endl; 39 | 40 | for (int i = 0; i < res.size(); ++i) { 41 | std::cout << "'" << GREEN << res[i].str() << RESET <<"'" << std::endl; 42 | } 43 | 44 | std::cout << YELLOW << "---------------------------END TEST containerStringSplit----------------------------------------------" << RESET << std::endl<< std::endl<< std::endl; 45 | } 46 | } 47 | 48 | void 49 | test2(AdvancedStringContainer& a, 50 | std::vector res, 51 | int num) 52 | { 53 | std::vector t; 54 | AdvancedStringContainer::trimEndAndRemoveNewlineChars(t, a); 55 | 56 | bool r = true; 57 | 58 | if (t.size() != res.size()) { 59 | r = false; 60 | } 61 | 62 | if (r) { 63 | for (int i = 0; i < t.size(); ++i) { 64 | if (t[i].str() != res[i].str()) { 65 | r = false; 66 | break; 67 | } 68 | } 69 | } 70 | 71 | if (!r) { 72 | std::cout << YELLOW << "---------------------------BEGIN TEST trimEndAndRemoveNewlineChars----------------------------------------------" << RESET << std::endl; 73 | std::cout << RED << "TEST " << num << " WAS A FAILURE" << RESET << std::endl; 74 | std::cout << "==== input was: ====" << std::endl; 75 | 76 | for (int i = 0; i < a.size(); ++i) { 77 | std::cout << "'" << YELLOW << a[i].str() << RESET << "'" << std::endl; 78 | } 79 | 80 | std::cout << "==== result was: ====" << std::endl; 81 | 82 | for (int i = 0; i < t.size(); ++i) { 83 | std::cout << "'" << RED << t[i].str() << RESET << "'" << std::endl; 84 | } 85 | 86 | std::cout << "==== but should be: ====" << std::endl; 87 | 88 | for (int i = 0; i < res.size(); ++i) { 89 | std::cout << "'" << GREEN << res[i].str() << RESET <<"'" << std::endl; 90 | } 91 | 92 | std::cout << YELLOW << "---------------------------END TEST trimEndAndRemoveNewlineChars----------------------------------------------" << RESET << std::endl<< std::endl<< std::endl; 93 | } 94 | } 95 | 96 | void 97 | test3(AdvancedStringContainer& a, 98 | std::vector res, 99 | int width, 100 | int num) 101 | { 102 | std::vector t; 103 | 104 | AdvancedStringContainer::terminal_rasterize(t, a, width); 105 | 106 | bool r = true; 107 | 108 | if (t.size() != res.size()) { 109 | r = false; 110 | } 111 | 112 | if (r) { 113 | 114 | for (int i = 0; i < t.size(); ++i) { 115 | if (t[i].str() != res[i].str()) { 116 | r = false; 117 | break; 118 | } 119 | } 120 | } 121 | 122 | if (!r) { 123 | std::cout << YELLOW << "---------------------------BEGIN TEST terminal_rasterize----------------------------------------------" << RESET << std::endl; 124 | std::cout << RED << "TEST " << num << " WAS A FAILURE" << RESET << std::endl; 125 | std::cout << "==== input was: ====" << std::endl; 126 | 127 | for (int i = 0; i < a.size(); ++i) { 128 | std::cout << "'" << YELLOW << a[i].str() << RESET << "'" << std::endl; 129 | } 130 | 131 | std::cout << "==== result was: ====" << std::endl; 132 | 133 | for (int i = 0; i < t.size(); ++i) { 134 | std::cout << "'" << RED << t[i].str() << RESET << "'" << std::endl; 135 | } 136 | 137 | std::cout << "==== but should be: ====" << std::endl; 138 | 139 | for (int i = 0; i < res.size(); ++i) { 140 | std::cout << "'" << GREEN << res[i].str() << RESET <<"'" << std::endl; 141 | } 142 | 143 | std::cout << YELLOW << "---------------------------END TEST terminal_rasterize----------------------------------------------" << RESET << std::endl<< std::endl<< std::endl; 144 | } 145 | } 146 | 147 | void 148 | test4(AdvancedString& a, 149 | AdvancedString& res, 150 | unsigned int b, 151 | unsigned int len, 152 | int num) 153 | { 154 | AdvancedString t = AdvancedString::substr(a, b, len); 155 | bool r = true; 156 | 157 | if (t.size() != res.size()) { 158 | r = false; 159 | } 160 | 161 | if (r) { 162 | for (int i=0; i < t.size(); ++i) { 163 | if (t.str() != res.str()) { 164 | r = false; 165 | break; 166 | } 167 | } 168 | } 169 | 170 | if (!r) { 171 | std::cout << YELLOW << "---------------------------BEGIN TEST AdvancedString::substr ----------------------------------------------" << RESET << std::endl; 172 | std::cout << RED << "TEST " << num << " WAS A FAILURE" << RESET << std::endl; 173 | std::cout << "==== input was: ====" << std::endl; 174 | std::cout << "'" << YELLOW << a.str() << RESET << "'" << std::endl; 175 | std::cout << "==== result was: ====" << std::endl; 176 | std::cout << "'" << RED << t.str() << RESET << "'" << std::endl; 177 | std::cout << "==== but should be: ====" << std::endl; 178 | std::cout << "'" << GREEN << res.str() << RESET <<"'" << std::endl; 179 | std::cout << YELLOW << "---------------------------END TEST AdvancedString::substr ----------------------------------------------" << RESET << std::endl<< std::endl<< std::endl; 180 | } 181 | } 182 | 183 | void 184 | test5(AdvancedStringContainer& a, 185 | AdvancedStringContainer& res, 186 | int b, 187 | int len, 188 | int num) 189 | { 190 | AdvancedStringContainer t; 191 | 192 | AdvancedStringContainer::substr(t, a, b, len); 193 | 194 | bool r = true; 195 | bool s = true; 196 | 197 | if (t.size() != res.size()) { 198 | r = false; 199 | s = false; 200 | } 201 | 202 | if (r) { 203 | for (int i = 0; i < t.size(); ++i) { 204 | if (t[i].str() != res[i].str()) { 205 | r = false; 206 | break; 207 | } 208 | } 209 | } 210 | 211 | if (!r) { 212 | std::cout << YELLOW << "---------------------------BEGIN TEST AdvancedStringContainer::substr ----------------------------------------------" << RESET << std::endl; 213 | std::cout << RED << "TEST " << num << " WAS A FAILURE" << RESET << std::endl; 214 | 215 | if (!s) { 216 | std::cout << RED << "size difference in AdvancedStringContainer t vs res = " << t.size() << " vs " << res.size() << RESET << std::endl; 217 | } 218 | 219 | std::cout << "==== input was: ====" << std::endl; 220 | std::cout << "'" << YELLOW << a.str() << RESET << "'" << std::endl; 221 | 222 | std::cout << "==== result was: ====" << std::endl; 223 | std::cout << "'" << RED << t.str() << RESET << "'" << std::endl; 224 | 225 | std::cout << "==== but should be: ====" << std::endl; 226 | std::cout << "'" << GREEN << res.str() << RESET <<"'" << std::endl; 227 | 228 | std::cout << YELLOW << "---------------------------END TEST AdvancedStringContainer::substr ----------------------------------------------" << RESET << std::endl<< std::endl<< std::endl; 229 | } 230 | } 231 | 232 | int 233 | main(int argc, char *argv[]) 234 | { 235 | { 236 | AdvancedStringContainer a; 237 | a << AdvancedString(""); 238 | 239 | std::vector res; 240 | AdvancedStringContainer e; 241 | e << AdvancedString(""); 242 | res.push_back(e); 243 | 244 | test1(a, res, 1); 245 | } 246 | 247 | { 248 | AdvancedStringContainer a; 249 | a << AdvancedString(" what"); 250 | 251 | std::vector res; 252 | AdvancedStringContainer e; 253 | e << AdvancedString(" what"); 254 | res.push_back(e); 255 | 256 | test1(a, res, 2); 257 | } 258 | 259 | { 260 | AdvancedStringContainer a; 261 | a << AdvancedString("foo \n"); 262 | 263 | std::vector res; 264 | AdvancedStringContainer e, e2; 265 | e << AdvancedString("foo "); 266 | res.push_back(e); 267 | e << AdvancedString(""); 268 | res.push_back(e2); 269 | 270 | test1(a, res, 3); 271 | } 272 | 273 | { 274 | AdvancedStringContainer a; 275 | a << AdvancedString("\n1\n\n1\n1\n1\n1\n"); 276 | a << AdvancedString("\n\n1\n\n\n\n1"); 277 | 278 | std::vector res; 279 | AdvancedStringContainer e; 280 | e << AdvancedString(""); 281 | 282 | AdvancedStringContainer e1; 283 | e1 << AdvancedString("1"); 284 | res.push_back(e); 285 | res.push_back(e1); 286 | res.push_back(e); 287 | 288 | res.push_back(e1); 289 | res.push_back(e1); 290 | res.push_back(e1); 291 | res.push_back(e1); 292 | res.push_back(e); 293 | res.push_back(e); 294 | 295 | res.push_back(e1); 296 | res.push_back(e); 297 | res.push_back(e); 298 | res.push_back(e); 299 | res.push_back(e1); 300 | 301 | test1(a, res, 4); 302 | } 303 | 304 | { 305 | AdvancedStringContainer a; 306 | a << AdvancedString("\n1\n\n1\n1\n1\n1\n\n\n1\n\n\n\n1"); 307 | 308 | std::vector res; 309 | AdvancedStringContainer e; 310 | e << AdvancedString(""); 311 | 312 | AdvancedStringContainer e1; 313 | e1 << AdvancedString("1"); 314 | 315 | res.push_back(e); 316 | res.push_back(e1); 317 | res.push_back(e); 318 | 319 | res.push_back(e1); 320 | res.push_back(e1); 321 | res.push_back(e1); 322 | res.push_back(e1); 323 | res.push_back(e); 324 | res.push_back(e); 325 | 326 | res.push_back(e1); 327 | res.push_back(e); 328 | res.push_back(e); 329 | res.push_back(e); 330 | res.push_back(e1); 331 | 332 | test1(a, res, 5); 333 | } 334 | 335 | { 336 | AdvancedStringContainer a; 337 | a << AdvancedString("hello\nworld"); 338 | 339 | std::vector res; 340 | AdvancedStringContainer e; 341 | e << AdvancedString("hello"); 342 | 343 | AdvancedStringContainer e1; 344 | e1 << AdvancedString("world"); 345 | 346 | res.push_back(e); 347 | res.push_back(e1); 348 | 349 | test1(a, res, 6); 350 | } 351 | 352 | { 353 | AdvancedStringContainer a; 354 | a << AdvancedString("\nhelloworld"); 355 | 356 | std::vector res; 357 | AdvancedStringContainer e; 358 | e << AdvancedString(""); 359 | 360 | AdvancedStringContainer e1; 361 | e1 << AdvancedString("helloworld"); 362 | 363 | res.push_back(e); 364 | res.push_back(e1); 365 | 366 | test1(a, res, 7); 367 | } 368 | 369 | { 370 | AdvancedStringContainer a; 371 | a << AdvancedString("\n11akf__\n__jaffka22") 372 | << AdvancedString("33akfkafk44\n55asdkfasdfk66") 373 | << AdvancedString("\n77afafaf88\n\n\nf"); 374 | 375 | std::vector res; 376 | 377 | AdvancedStringContainer e, e1, e2, e3, e4, e5, e6, e7; 378 | e << AdvancedString(""); 379 | res.push_back(e); 380 | 381 | e1 << AdvancedString("11akf__"); 382 | res.push_back(e1); 383 | 384 | e2 << AdvancedString("__jaffka2233akfkafk44"); 385 | res.push_back(e2); 386 | 387 | e3 << AdvancedString("55asdkfasdfk66"); 388 | res.push_back(e3); 389 | 390 | e4 << AdvancedString("77afafaf88"); 391 | res.push_back(e4); 392 | 393 | e5 << AdvancedString(""); 394 | res.push_back(e5); 395 | 396 | e6 << AdvancedString(""); 397 | res.push_back(e6); 398 | 399 | e7 << AdvancedString("f"); 400 | res.push_back(e7); 401 | 402 | test1(a, res,8); 403 | } 404 | 405 | { 406 | AdvancedStringContainer a; 407 | a << AdvancedString("\n"); 408 | 409 | std::vector res; 410 | AdvancedStringContainer e, e2; 411 | e << AdvancedString(""); 412 | res.push_back(e); 413 | 414 | e << AdvancedString(""); 415 | res.push_back(e2); 416 | 417 | test1(a, res, 9); 418 | } 419 | 420 | { 421 | AdvancedStringContainer a; 422 | a << AdvancedString("\n"); 423 | 424 | std::vector res; 425 | AdvancedStringContainer e; 426 | e << AdvancedString(""); 427 | 428 | res.push_back(e); 429 | res.push_back(e); 430 | 431 | test1(a, res, 10); 432 | } 433 | 434 | { 435 | AdvancedStringContainer a; 436 | a << AdvancedString("**this should be colored in MAGENTA**\n", COLOR_MAGENTA); 437 | a << AdvancedString("**this should be colored in GREEN**\n", COLOR_GREEN); 438 | a << AdvancedString("----------------------1111111111111111111111111122222222222222222222222222222222233333333333333333333333333333333334444444\n", COLOR_GREEN); 439 | a << AdvancedString("\n\na"); 440 | 441 | std::vector res; 442 | AdvancedStringContainer e, e1, e2, e3, e4; 443 | 444 | e << AdvancedString("**this should be colored in MAGENTA**"); 445 | res.push_back(e); 446 | 447 | e1 << AdvancedString("**this should be colored in GREEN**"); 448 | res.push_back(e1); 449 | 450 | e2 << AdvancedString("----------------------1111111111111111111111111122222222222222222222222222222222233333333333333333333333333333333334444444"); 451 | res.push_back(e2); 452 | 453 | e3 << AdvancedString(""); 454 | res.push_back(e3); 455 | res.push_back(e3); 456 | 457 | e4 << AdvancedString("a"); 458 | res.push_back(e4); 459 | 460 | test1(a, res, 11); 461 | } 462 | 463 | { 464 | AdvancedStringContainer a; 465 | a << AdvancedString(" foo\n"); 466 | a << AdvancedString(" bar "); 467 | a << AdvancedString(" "); 468 | 469 | std::vector res; 470 | AdvancedStringContainer e,e1; 471 | e << AdvancedString(" foo"); 472 | res.push_back(e); 473 | 474 | e1 << AdvancedString(" bar "); 475 | e1 << AdvancedString(" "); 476 | res.push_back(e1); 477 | 478 | test1(a, res, 12); 479 | } 480 | 481 | { 482 | AdvancedStringContainer b; 483 | b << AdvancedString("linux "); 484 | b << AdvancedString("for \n the "); 485 | b << AdvancedString("ppl "); 486 | 487 | std::vector res; 488 | AdvancedStringContainer e,e1; 489 | 490 | e << AdvancedString("linux for"); 491 | e1 << AdvancedString(" the ppl"); 492 | res.push_back(e); 493 | res.push_back(e1); 494 | 495 | test2(b, res, 50); 496 | } 497 | 498 | { 499 | AdvancedStringContainer b; 500 | b << AdvancedString(""); 501 | 502 | std::vector res; 503 | AdvancedStringContainer e; 504 | e << AdvancedString(""); 505 | res.push_back(e); 506 | 507 | test2(b, res, 51); 508 | } 509 | 510 | { 511 | AdvancedStringContainer b; 512 | b << AdvancedString(" "); 513 | 514 | std::vector res; 515 | AdvancedStringContainer e; 516 | e << AdvancedString(""); 517 | res.push_back(e); 518 | 519 | test2(b, res, 52); 520 | } 521 | 522 | { 523 | AdvancedStringContainer b; 524 | b << AdvancedString(" "); 525 | b << AdvancedString(""); 526 | b << AdvancedString(""); 527 | b << AdvancedString(" "); 528 | 529 | std::vector res; 530 | AdvancedStringContainer e; 531 | e << AdvancedString(""); 532 | res.push_back(e); 533 | 534 | test2(b, res, 53); 535 | } 536 | 537 | { 538 | AdvancedStringContainer b; 539 | b << AdvancedString(" "); 540 | b << AdvancedString(" \n "); 541 | b << AdvancedString(" foo\n"); 542 | b << AdvancedString(" bar "); 543 | 544 | std::vector res; 545 | AdvancedStringContainer e,e1,e2,e3; 546 | e << AdvancedString(""); 547 | res.push_back(e); 548 | e1 << AdvancedString(" foo"); 549 | res.push_back(e1); 550 | e2 << AdvancedString(" bar"); 551 | res.push_back(e2); 552 | 553 | test2(b, res, 54); 554 | } 555 | 556 | { 557 | AdvancedStringContainer b; 558 | b << AdvancedString("**this should be colored in MAGENTA**\n", COLOR_MAGENTA); 559 | b << AdvancedString("**this should be colored in GREEN**\n", COLOR_GREEN); 560 | b << AdvancedString("----------------------1111111111111111111111111122222222222222222222222222222222233333333333333333333333333333333334444444\n", COLOR_GREEN); 561 | b << AdvancedString("\n\na"); 562 | 563 | std::vector res; 564 | AdvancedStringContainer e,e1,e2,e3,e4; 565 | e << AdvancedString("**this should be colored in MAGENTA**"); 566 | res.push_back(e); 567 | 568 | e1 << AdvancedString("**this should be colored in GREEN**"); 569 | res.push_back(e1); 570 | 571 | e2 << AdvancedString("----------------------1111111111111111111111111122222222222222222222222222222222233333333333333333333333333333333334444444"); 572 | res.push_back(e2); 573 | 574 | e3 << AdvancedString(""); 575 | res.push_back(e3); 576 | res.push_back(e3); 577 | e4 << AdvancedString("a"); 578 | 579 | res.push_back(e4); 580 | 581 | test2(b, res, 55); 582 | } 583 | 584 | { 585 | AdvancedStringContainer b; 586 | b << AdvancedString(" foo\n"); 587 | b << AdvancedString(" bar "); 588 | b << AdvancedString(" "); 589 | 590 | std::vector res; 591 | AdvancedStringContainer e1,e2; 592 | e1 << AdvancedString(" foo"); 593 | res.push_back(e1); 594 | 595 | e2 << AdvancedString(" bar"); 596 | res.push_back(e2); 597 | 598 | test2(b, res, 56); 599 | } 600 | 601 | { 602 | AdvancedStringContainer b; 603 | b << AdvancedString("123 \n"); 604 | b << AdvancedString(" \n"); 605 | b << AdvancedString("1lk2j312k3j"); 606 | 607 | std::vector res; 608 | AdvancedStringContainer e1,e2,e3; 609 | 610 | e1 << AdvancedString("123"); 611 | res.push_back(e1); 612 | 613 | e2 << AdvancedString(""); 614 | res.push_back(e2); 615 | 616 | e3 << AdvancedString("1lk2j312k3j"); 617 | res.push_back(e3); 618 | 619 | test2(b, res, 57); 620 | } 621 | 622 | ////////////////////////// terminal_rasterize /////////////////////// 623 | 624 | { 625 | AdvancedStringContainer c; 626 | c << AdvancedString("\nhello worldhow are you today?"); 627 | c << AdvancedString("this is funny stuff\n"); 628 | c << AdvancedString("bye bye world"); 629 | 630 | std::vector res; 631 | AdvancedStringContainer e, e0, e1, e2, e3; 632 | 633 | e << AdvancedString(std::string(17, ' ')); 634 | res.push_back(e); 635 | 636 | e0 << AdvancedString("hello worldhow ar"); 637 | res.push_back(e0); 638 | 639 | e1 << AdvancedString("e you today?this "); 640 | res.push_back(e1); 641 | 642 | e2 << AdvancedString("is funny stuff "); 643 | res.push_back(e2); 644 | 645 | e3 << AdvancedString("bye bye world "); 646 | res.push_back(e3); 647 | 648 | test3(c, res, 17, 71); 649 | } 650 | 651 | { 652 | AdvancedStringContainer c; 653 | c << AdvancedString("\n\nmore and more\n questions arise?"); 654 | c << AdvancedString("due to broken code\n\n"); 655 | c << AdvancedString("so \nbye bye world\n"); 656 | 657 | std::vector res; 658 | AdvancedStringContainer e, e0, e1, e2, e3, e4, e5; 659 | 660 | e << AdvancedString(std::string(17, ' ')); 661 | res.push_back(e); 662 | res.push_back(e); 663 | 664 | e0 << AdvancedString("more and more "); 665 | res.push_back(e0); 666 | 667 | e1 << AdvancedString(" questions arise?"); 668 | res.push_back(e1); 669 | 670 | e2 << AdvancedString("due to broken cod"); 671 | res.push_back(e2); 672 | 673 | e3 << AdvancedString("e "); 674 | res.push_back(e3); 675 | res.push_back(e); 676 | 677 | e4 << AdvancedString("so "); 678 | res.push_back(e4); 679 | 680 | e5 << AdvancedString("bye bye world "); 681 | res.push_back(e5); 682 | // res.push_back(e); 683 | 684 | test3(c, res, 17, 72); 685 | } 686 | 687 | { 688 | AdvancedStringContainer c; 689 | c << AdvancedString("**this should be colored in MAGENTA**\n", COLOR_MAGENTA); 690 | c << AdvancedString("**this should be colored in GREEN**\n", COLOR_GREEN); 691 | c << AdvancedString("----------------------1111111111111111111111111122222222222222222222222222222222233333333333333333333333333333333334444444\n", COLOR_GREEN); 692 | c << AdvancedString("\n\na"); 693 | 694 | std::vector res; 695 | AdvancedStringContainer e,e0,e1,e2,e3,e4,e5; 696 | 697 | e << AdvancedString(std::string(50, ' ')); 698 | e0 << AdvancedString("**this should be colored in MAGENTA** "); 699 | res.push_back(e0); 700 | 701 | e1 << AdvancedString("**this should be colored in GREEN** "); 702 | res.push_back(e1); 703 | 704 | e2 << AdvancedString("----------------------1111111111111111111111111122"); 705 | res.push_back(e2); 706 | 707 | e3 << AdvancedString("22222222222222222222222222222223333333333333333333"); 708 | res.push_back(e3); 709 | 710 | e4 << AdvancedString("3333333333333334444444 "); 711 | res.push_back(e4); 712 | res.push_back(e); 713 | res.push_back(e); 714 | 715 | e5 << AdvancedString("a "); 716 | res.push_back(e5); 717 | 718 | test3(c, res, 50, 73); 719 | std::vector buf; 720 | 721 | // AdvancedStringContainer::trimEndAndRemoveNewlineChars(buf, c); 722 | // std::cout << "==== NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN: ====" << std::endl; 723 | // for (int i=0; i < buf.size(); ++i) { 724 | // std::cout << "'" << YELLOW << buf[i].str() << RESET << "'" << std::endl; 725 | // } 726 | } 727 | 728 | ////////////////////////// AdvancedString::substr ////////////////////// 729 | 730 | { 731 | AdvancedString c("hello world"); 732 | AdvancedString res("hello"); 733 | test4(c, res, 0, 5, 90); 734 | } 735 | 736 | { 737 | AdvancedString c("hello world"); 738 | AdvancedString res(""); 739 | test4(c, res, 0, 0, 91); 740 | } 741 | 742 | { 743 | AdvancedString c("hello world"); 744 | AdvancedString res(" world"); 745 | test4(c, res, 5, 55, 92); 746 | } 747 | 748 | { 749 | AdvancedString c("hello world"); 750 | AdvancedString res(""); 751 | test4(c, res, 55, 55, 93); 752 | } 753 | 754 | { 755 | AdvancedString c(""); 756 | AdvancedString res(""); 757 | test4(c, res, 55, 55, 94); 758 | } 759 | 760 | { 761 | AdvancedString c("123"); 762 | AdvancedString res("2"); 763 | test4(c, res, 1, 1, 95); 764 | } 765 | 766 | //////////////////// AdvancedStringContainer::substr /////////////////// 767 | 768 | { 769 | AdvancedStringContainer c; 770 | c << "hello world" << ", how are " << "you"; 771 | 772 | AdvancedStringContainer res; 773 | res << "hello world"; 774 | 775 | test5(c, res, 0, 11, 110); 776 | } 777 | 778 | { 779 | AdvancedStringContainer c; 780 | c << "hello world" << ", how are " << "you"; 781 | 782 | AdvancedStringContainer res; 783 | res << "llo world, how"; 784 | 785 | test5(c, res, 2, 14, 111); 786 | } 787 | 788 | { 789 | AdvancedStringContainer c; 790 | c << "hello world" << ", how are " << "you"; 791 | 792 | AdvancedStringContainer res; 793 | res << "llo world, how are y"; 794 | 795 | test5(c, res, 2, 20, 112); 796 | } 797 | 798 | { 799 | AdvancedStringContainer c; 800 | c << "" << "hello world" << "" << ", how are " << "you"; 801 | 802 | AdvancedStringContainer res; 803 | res << "llo world, how are y"; 804 | 805 | test5(c, res, 2, 20, 113); 806 | } 807 | 808 | { 809 | AdvancedStringContainer c; 810 | c << ""; 811 | 812 | AdvancedStringContainer res; // should be empty 813 | 814 | test5(c, res, 0, 11, 114); 815 | } 816 | 817 | { 818 | AdvancedStringContainer c; 819 | c << "" << "" << ""; 820 | 821 | AdvancedStringContainer res; // should be empty 822 | 823 | test5(c, res, 0, 11, 115); 824 | } 825 | 826 | { 827 | AdvancedStringContainer c; 828 | c << "" << "" << "" << "f" << ""; 829 | 830 | AdvancedStringContainer res; 831 | res << "f"; 832 | 833 | test5(c, res, 0, 1, 116); 834 | } 835 | 836 | exit(0); 837 | } 838 | 839 | --------------------------------------------------------------------------------