/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 * Jonathan Schleifer <js@heap.zone> * * All rights reserved. * * This file is part of ObjFW. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE.QPL included in * the packaging of this file. * * Alternatively, it may be distributed under the terms of the GNU General * Public License, either version 2 or 3, which can be found in the file * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #include "config.h" #include <math.h> #include <unistd.h> #ifdef HAVE_SYS_IOCTL_H # include <sys/ioctl.h> #endif #ifdef HAVE_SYS_TERMIOS_H # include <sys/termios.h> #endif #import "OFDate.h" #import "OFStdIOStream.h" #import "OFTimer.h" #import "ProgressBar.h" #define GIBIBYTE (1024 * 1024 * 1024) #define MEBIBYTE (1024 * 1024) #define KIBIBYTE (1024) #define UPDATE_INTERVAL 0.1 @implementation ProgressBar - initWithLength: (intmax_t)length resumedFrom: (intmax_t)resumedFrom { self = [super init]; @try { void *pool = objc_autoreleasePoolPush(); _length = length; _resumedFrom = resumedFrom; _startDate = [[OFDate alloc] init]; _lastReceivedDate = [[OFDate alloc] init]; _drawTimer = [[OFTimer scheduledTimerWithTimeInterval: UPDATE_INTERVAL target: self selector: @selector(draw) repeats: true] retain]; _BPSTimer = [[OFTimer scheduledTimerWithTimeInterval: 1.0 target: self selector: @selector( calculateBPSAndETA) repeats: true] retain]; objc_autoreleasePoolPop(pool); } @catch (id e) { [self release]; @throw e; } return self; } - (void)dealloc { [self stop]; [_startDate release]; [_lastReceivedDate release]; [_drawTimer release]; [_BPSTimer release]; [super dealloc]; } - (void)setReceived: (intmax_t)received { _received = received; } - (void)_drawProgress { float bars, percent; unsigned short barWidth; #if defined(HAVE_SYS_IOCTL_H) && defined(TIOCGWINSZ) struct winsize ws; if (ioctl(0, TIOCGWINSZ, &ws) == 0 && ws.ws_col > 37) barWidth = ws.ws_col - 37; else barWidth = 0; #else barWidth = 43; #endif bars = (float)(_resumedFrom + _received) / (float)(_resumedFrom + _length) * barWidth; percent = (float)(_resumedFrom + _received) / (float)(_resumedFrom + _length) * 100; [of_stdout writeString: @"\r ▕"]; for (size_t i = 0; i < (size_t)bars; i++) [of_stdout writeString: @"█"]; if (bars < barWidth) { float remainder = bars - floorf(bars); if (remainder >= 0.875) [of_stdout writeString: @"▉"]; else if (remainder >= 0.75) [of_stdout writeString: @"▊"]; else if (remainder >= 0.625) [of_stdout writeString: @"▋"]; else if (remainder >= 0.5) [of_stdout writeString: @"▌"]; else if (remainder >= 0.375) [of_stdout writeString: @"▍"]; else if (remainder >= 0.25) [of_stdout writeString: @"▎"]; else if (remainder >= 0.125) [of_stdout writeString: @"▏"]; else [of_stdout writeString: @" "]; for (size_t i = 0; i < barWidth - (size_t)bars - 1; i++) [of_stdout writeString: @" "]; } [of_stdout writeFormat: @"▏ %6.2f%% ", percent]; if (percent == 100) { double timeInterval = -[_startDate timeIntervalSinceNow]; _BPS = (float)_received / (float)timeInterval; _ETA = timeInterval; } if (isinf(_ETA)) [of_stdout writeString: @"--:--:-- "]; else if (_ETA >= 99 * 3600) [of_stdout writeFormat: @"%4.2f d ", _ETA / (24 * 3600)]; else [of_stdout writeFormat: @"%2u:%02u:%02u ", (uint8_t)(_ETA / 3600), (uint8_t)(_ETA / 60) % 60, (uint8_t)_ETA % 60]; if (_BPS >= GIBIBYTE) [of_stdout writeFormat: @"%7.2f GiB/s", _BPS / GIBIBYTE]; else if (_BPS >= MEBIBYTE) [of_stdout writeFormat: @"%7.2f MiB/s", _BPS / MEBIBYTE]; else if (_BPS >= KIBIBYTE) [of_stdout writeFormat: @"%7.2f KiB/s", _BPS / KIBIBYTE]; else [of_stdout writeFormat: @"%7.2f B/s ", _BPS]; } - (void)_drawReceived { if (_resumedFrom + _received >= GIBIBYTE) [of_stdout writeFormat: @"\r %7.2f GiB ", (float)(_resumedFrom + _received) / GIBIBYTE]; else if (_resumedFrom + _received >= MEBIBYTE) [of_stdout writeFormat: @"\r %7.2f MiB ", (float)(_resumedFrom + _received) / MEBIBYTE]; else if (_resumedFrom + _received >= KIBIBYTE) [of_stdout writeFormat: @"\r %7.2f KiB ", (float)(_resumedFrom + _received) / KIBIBYTE]; else [of_stdout writeFormat: @"\r %jd bytes ", _resumedFrom + _received]; if (_stopped) _BPS = (float)_received / -(float)[_startDate timeIntervalSinceNow]; if (_BPS >= GIBIBYTE) [of_stdout writeFormat: @"%7.2f GiB/s", _BPS / GIBIBYTE]; else if (_BPS >= MEBIBYTE) [of_stdout writeFormat: @"%7.2f MiB/s", _BPS / MEBIBYTE]; else if (_BPS >= KIBIBYTE) [of_stdout writeFormat: @"%7.2f KiB/s", _BPS / KIBIBYTE]; else [of_stdout writeFormat: @"%7.2f B/s ", _BPS]; } - (void)draw { if (_length > 0) [self _drawProgress]; else [self _drawReceived]; } - (void)calculateBPSAndETA { _BPS = (float)(_received - _lastReceived) / -(float)[_lastReceivedDate timeIntervalSinceNow]; _ETA = (double)(_length - _received) / _BPS; _lastReceived = _received; [_lastReceivedDate release]; _lastReceivedDate = [[OFDate alloc] init]; } - (void)stop { [_drawTimer invalidate]; [_BPSTimer invalidate]; _stopped = true; } @end