summaryrefslogtreecommitdiffstats
path: root/grub-core/lib/progress.c
blob: 4b7cbbca6ddce81071584207d74868ee91a1d224 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/* progress.c - show loading progress */
/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2013  Free Software Foundation, Inc.
 *
 *  GRUB is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  GRUB is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <grub/types.h>
#include <grub/time.h>
#include <grub/term.h>
#include <grub/dl.h>
#include <grub/misc.h>
#include <grub/normal.h>
#include <grub/net.h>

GRUB_MOD_LICENSE ("GPLv3+");

#define UPDATE_INTERVAL 800

static void
grub_file_progress_hook_real (grub_disk_addr_t sector __attribute__ ((unused)),
                              unsigned offset __attribute__ ((unused)),
                              unsigned length, void *data)
{
  static int call_depth = 0;
  grub_uint64_t now;
  static grub_uint64_t last_progress_update_time;
  grub_file_t file = data;
  const char *e;
  file->progress_offset += length;

  if (call_depth)
    return;

  e = grub_env_get ("enable_progress_indicator");
  if (e && e[0] == '0') {
    return;
  }

  call_depth = 1;
  now = grub_get_time_ms ();

  if (((now - last_progress_update_time > UPDATE_INTERVAL) &&
       (file->progress_offset - file->offset > 0)) ||
      (file->progress_offset == file->size))
    {
      static char buffer[80];
      struct grub_term_output *term;
      const char *partial_file_name;

      unsigned long long percent;
      grub_uint64_t current_speed;

      if (now - file->last_progress_time < 10)
	current_speed = 0;
      else
	current_speed = grub_divmod64 ((file->progress_offset
					- file->last_progress_offset)
				       * 100ULL * 1000ULL,
				       now - file->last_progress_time, 0);

      if (file->size == 0)
	percent = 100;
      else
	percent = grub_divmod64 (100 * file->progress_offset,
				 file->size, 0);

      /* grub_net_fs_open() saves off partial file structure before name is initialized.
         It already saves passed file name in net structure so just use it in this case.
       */
      if (file->device->net)
	partial_file_name = grub_strrchr (file->device->net->name, '/');
      else if (file->name) /* grub_file_open() may leave it as NULL */
	partial_file_name = grub_strrchr (file->name, '/');
      else
	partial_file_name = NULL;
      if (partial_file_name)
	partial_file_name++;
      else
	partial_file_name = "";

      file->estimated_speed = (file->estimated_speed + current_speed) >> 1;

      grub_snprintf (buffer, sizeof (buffer), "      [ %.20s  %s  %llu%%  ",
                     partial_file_name,
                     grub_get_human_size (file->progress_offset,
                                          GRUB_HUMAN_SIZE_NORMAL),
                     (unsigned long long) percent);

      char *ptr = buffer + grub_strlen (buffer);
      grub_snprintf (ptr, sizeof (buffer) - (ptr - buffer), "%s ]",
                     grub_get_human_size (file->estimated_speed,
                                          GRUB_HUMAN_SIZE_SPEED));

      grub_size_t len = grub_strlen (buffer);
      FOR_ACTIVE_TERM_OUTPUTS (term)
        {
          if (term->progress_update_counter++ > term->progress_update_divisor
	      || (file->progress_offset == file->size
		  && term->progress_update_divisor
		  != (unsigned) GRUB_PROGRESS_NO_UPDATE))
            {
              struct grub_term_coordinate old_pos = grub_term_getxy (term);
              struct grub_term_coordinate new_pos = old_pos;
              new_pos.x = grub_term_width (term) - len - 1;

              grub_term_gotoxy (term, new_pos);
	      grub_puts_terminal (buffer, term);
              grub_term_gotoxy (term, old_pos);

              term->progress_update_counter = 0;

	      if (term->refresh)
		term->refresh (term);
            }
        }

      file->last_progress_offset = file->progress_offset;
      file->last_progress_time = now;
      last_progress_update_time = now;
    }
  call_depth = 0;
}

GRUB_MOD_INIT(progress)
{
  grub_file_progress_hook = grub_file_progress_hook_real;
}

GRUB_MOD_FINI(progress)
{
  grub_file_progress_hook = 0;
}