1

Тема: Индикатор использования ЦП и ОЗУ(пример Frame + ProgressBar)

Пример показывает уровень загрузки процессора и использования оперативной памяти.
misc.php?action=pun_attachment&item=112&download=0
Для получения значения тактовой частоты процессора используется системная функция GetCPUClock.
Однако, эта функция может возвращать значения только в пределах до 4 ГГц.
В примере используется системная функция SetWindowZOrder для установки положения окна поверх всех окон, это удобно — окно всегда наверху.

program CPUMemUsageIndicator;

uses
  KolibriOS;

const
  WINDOW_BORDER_SIZE = 5;

// Константы положения окна относительно других окон:
  ZPOS_DESKTOP     = -2; // на самом заднем плане
  ZPOS_ALWAYS_BACK = -1; // позади всех окон
  ZPOS_NORMAL      = 0;  // обычное
  ZPOS_ALWAYS_TOP  = 1;  // поверх всех окон

// Frame flags
  FR_CAPTION = 1;
  FR_DOUBLE  = 0;
  FR_RAISED  = 2;
  FR_SUNKEN  = 4;
  FR_ETCHED  = 6;
  FR_RIDGED  = 8;
  FR_FILLED  = 16;

// Frame text position
  FR_TEXT_POS_BOTTOM = 1;
  FR_TEXT_POS_TOP    = 0;

type
  TProgressBar = packed record
    Value:         LongWord;
    Left:          LongInt;
    Top:           LongInt;
    Width:         LongWord;
    Height:        LongWord;
    Style:         LongWord;
    Min:           LongWord;
    Max:           LongWord;
    BackColor:     LongWord;
    ProgressColor: LongWord;
    FrameColor:    LongWord;
  end;

  TFrame = packed record
    Style:        LongWord;
    Width:        Word;
    Left:         SmallInt;
    Height:       Word;
    Top:          SmallInt;
    OuterColor:   LongWord;
    InnerColor:   LongWord;
    Flags:        LongWord;
    Text:         PAnsiChar;
    TextPosition: LongWord;
    Font:         LongWord;
    FontHeight:   LongWord;
    ForeColor:    LongWord;
    BackColor:    LongWord;
  end;


var
  CPUFrame: TFrame = (
    Style: 0;
    Width: 271;
    Left: 8;
    Height: 57;
    Top: 13;
    OuterColor: $00FFFFFF;
    InnerColor: $00808080;
    Flags: FR_ETCHED + FR_CAPTION + FR_FILLED;
    Text: 'Процессора';
    TextPosition: FR_TEXT_POS_TOP;
    Font: 1;
    FontHeight: 16;
    ForeColor: $00000070;
    BackColor: $00D0D0D0;
  );

  MemFrame: TFrame = (
    Style: 0;
    Width: 271;
    Left: 8;
    Height: 57;
    Top: 84;
    OuterColor: $00FFFFFF;
    InnerColor: $00808080;
    Flags: FR_ETCHED + FR_CAPTION + FR_FILLED;
    Text: 'Памяти';
    TextPosition: FR_TEXT_POS_TOP;
    Font: 1;
    FontHeight: 16;
    ForeColor: $00000070;
    BackColor: $00D0D0D0;
  );

  CPUProgressBar: TProgressBar = (
    Value: 0;
    Left: 56;
    Top: 37;
    Width: 215;
    Height: 17;
    Style: 0;
    Min: 0;
    Max: 100;
    BackColor: $00FFFFFF;
    ProgressColor: $0000007F;
    FrameColor: $007F7F7F;
  );

  MemProgressBar: TProgressBar = (
    Value: 0;
    Left: 56;
    Top: 108;
    Width: 215;
    Height: 17;
    Style: 0;
    Min: 0;
    Max: 100;
    BackColor: $00FFFFFF;
    ProgressColor: $0000007F;
    FrameColor: $007F7F7F;
  );

  BoxLib: Pointer;
  progressbar_draw: procedure(var ProgressBar: TProgressBar); stdcall;
  frame_draw:       procedure(var Frame: TFrame); stdcall;

  WndLeft, WndTop: LongInt;
  WndWidth, WndHeight: LongWord;

// беззнаковое число Number в строку Buffer, размер буфера > 10
procedure UIntToStr(Number: LongInt; Buffer: PAnsiChar);
asm
         push   edi
         push   esi
         mov    eax, Number
         mov    ecx, 10
         mov    esi, Buffer
         mov    edi, esi
         add    esi, ecx
         mov    byte ptr [esi], 0
@next:
         xor    edx, edx
         div    ecx
         dec    esi
         add    dl, 48
         mov    [esi], dl
         test   eax, eax
         jnz    @next
         mov    ecx, esi
         sub    ecx, edi
         rep    movsb
         pop    esi
         pop    edi
end;

procedure DrawIndicators;
var
  ValueBuffer: array [0..10] of AnsiChar;
begin
  // если тактовая частота больше 4 ГГц(что уже не редкость), то
  // системная функция GetCPUClock вернёт неверное значение
  // "shr 8" чтобы не было переполнения при умножении на 100
  // определяем значения в процентах
  CPUProgressBar.Value := 100 - (GetIdleTime shr 8) * 100 div (GetCPUClock shr 8);
  MemProgressBar.Value := 100 - (GetFreeMemory shr 8) * 100 div (GetAvailableMemory shr 8);
  progressbar_draw(CPUProgressBar);
  progressbar_draw(MemProgressBar);
  DrawText(16, CPUProgressBar.Top, '   %', CPUFrame.ForeColor, CPUFrame.BackColor, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);
  UIntToStr(CPUProgressBar.Value, ValueBuffer);
  DrawText(16, CPUProgressBar.Top, ValueBuffer, CPUFrame.ForeColor, CPUFrame.BackColor, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);
  DrawText(16, MemProgressBar.Top, '   %', MemFrame.ForeColor, MemFrame.BackColor, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);
  UIntToStr(MemProgressBar.Value, ValueBuffer);
  DrawText(16, MemProgressBar.Top, ValueBuffer, MemFrame.ForeColor, MemFrame.BackColor, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);
end;

begin
  WndHeight := 149 + GetSkinHeight + WINDOW_BORDER_SIZE;
  WndWidth  := 286 + WINDOW_BORDER_SIZE * 2;

  BoxLib           := LoadLibrary('/sys/lib/box_lib.obj');
  frame_draw       := GetProcAddress(BoxLib, 'frame_draw');
  progressbar_draw := GetProcAddress(BoxLib, 'progressbar_draw');

  with GetScreenSize do
  begin
    WndLeft := (Width - WndWidth) div 2;
    WndTop  := (Height - WndHeight) div 2;
  end;

  SetEventMask(EM_REDRAW or EM_BUTTON);
  SetWindowZOrder($FFFFFFFF, ZPOS_ALWAYS_TOP);

  while True do
    case WaitEventByTime(25) of
      REDRAW_EVENT:
        begin
          BeginDraw;
          DrawWindow(WndLeft, WndTop, WndWidth, WndHeight, 'Индикатор использования', $00CFCFCF,
            WS_SKINNED_FIXED + WS_CLIENT_COORDS + WS_CAPTION, CAPTION_MOVABLE);
          frame_draw(CPUFrame);
          frame_draw(MemFrame);
          DrawIndicators;
          EndDraw;
        end;
      BUTTON_EVENT:
        Break;
    else
      DrawIndicators;
    end;
end.

Также прикладываю уже скомпилированный пример CPUMemUsageIndicator.kex

Post's attachments

Иконка вложений CPUMemUsageIndicator.kex 1.02 Кб, 69 скачиваний с 2021-12-19 

CPUMemUsageIndicator.PNG, 3.76 Кб, 297 x 179
CPUMemUsageIndicator.PNG 3.76 Кб, 73 скачиваний с 2021-12-19 

2

Re: Индикатор использования ЦП и ОЗУ(пример Frame + ProgressBar)

Падает на полсденей ночной сбрке eng, нужна какая-то обновленная системная бибилотека?

Как вариант для улучшения:
1. Системные цвета (для фрейма есть color_light, color_dark)
2. Окно без скина (типа виджет, как /sys/pipet)
3. Виджет на рабочем столе (как конки: через рисование на фоне, либо как z-top: под всеми)

3

Re: Индикатор использования ЦП и ОЗУ(пример Frame + ProgressBar)

Leency пишет:

Падает на полсденей ночной сбрке eng, нужна какая-то обновленная системная бибилотека?

Нет, обновлённая библиотека не нужна.
У меня есть вариант, где примерно может падать.
Кстати, в самом исходнике есть даже комментарий на этот счёт:

  // если тактовая частота больше 4 ГГц(что уже не редкость), то
  // системная функция GetCPUClock вернёт неверное значение
  // "shr 8" чтобы не было переполнения при умножении на 100
  // определяем значения в процентах
  CPUProgressBar.Value := 100 - (GetIdleTime shr 8) * 100 div (GetCPUClock shr 8);
  MemProgressBar.Value := 100 - (GetFreeMemory shr 8) * 100 div (GetAvailableMemory shr 8);

Даже если падает не тут, то всё равно есть проблема с системной функцией GetCPUClock(SysFn18.5).
Для процессоров с частотой более 4 ГГц вернёт неверное значение, просто потому что оно не влезет в 32-битный регистр.
Думаю, что ядро KolibriOS вполне могло бы возвращать значение в паре регистров edx:eax.

Leency пишет:

Как вариант для улучшения:
1. Системные цвета (для фрейма есть color_light, color_dark)

Да, вполне хорошая идея.

Leency пишет:

Как вариант для улучшения:
2. Окно без скина (типа виджет, как /sys/pipet)

Если это удобнее, то, наверное, можно.
Только тогда надписи надо будет изменить, ведь в заголовке указано, что именно отображается.

Leency пишет:

Как вариант для улучшения:
3. Виджет на рабочем столе (как конки: через рисование на фоне, либо как z-top: под всеми)

По поводу положения окна: сейчас сделано наоборот — используется системная функция SetWindowZOrder для установки положения окна поверх всех окон, но можно сделать настраиваемо: чтобы было под всеми надо использовать флаг ZPOS_DESKTOP вместо ZPOS_ALWAYS_TOP.

Насчёт рисования на фоне трудно сказать, потому что фон может меняться пользователем, тогда придётся следить, чтобы не сливалось с фоном.
Ну и позиция для рисования тоже под вопросом: вдруг кто-то ещё захочет выводить информацию на фон, тогда как это узнать? С окнами проще — за ними само ядро следит. Если только завести специальный ini-файл, хранящий значения Left, Top, Width, Height для всех подобных виджетов.

Добавлено 2021-12-27 в 10:58

Я  попробовал приблизительно определять частоту самостоятельно без системной функции, это можно сделать так

function GetCPUMegaHerz: LongWord;
const
  SLEEP_TIME = 33;

var
  BeginClock, EndClock: Int64;

  function RDTSC: int64;
  asm
    RDTSC
  end;

begin
  BeginClock := RDTSC;
  Sleep(SLEEP_TIME);
  EndClock := RDTSC;
  Result := Int64(EndClock - BeginClock);
  Result := Result div (10000 * SLEEP_TIME);
end;

Похоже есть баг в ядре: после установки

SetWindowZOrder($FFFFFFFF, ZPOS_ALWAYS_TOP);

окну не приходят события перерисовки, когда меняется скин окна(попробовал использовать системные цвета для рамок и текста), если закомментировать строку, то приходят.
Вот полностью исходник:

program CPUMemUsageIndicator;

uses
  KolibriOS;

const
  WINDOW_BORDER_SIZE = 5;

// Константы положения окна относительно других окон:
  ZPOS_DESKTOP     = -2; // на самом заднем плане
  ZPOS_ALWAYS_BACK = -1; // позади всех окон
  ZPOS_NORMAL      = 0;  // обычное
  ZPOS_ALWAYS_TOP  = 1;  // поверх всех окон

// Frame flags
  FR_CAPTION = 1;
  FR_DOUBLE  = 0;
  FR_RAISED  = 2;
  FR_SUNKEN  = 4;
  FR_ETCHED  = 6;
  FR_RIDGED  = 8;
  FR_FILLED  = 16;

// Frame text position
  FR_TEXT_POS_BOTTOM = 1;
  FR_TEXT_POS_TOP    = 0;

type
  TProgressBar = packed record
    Value:         LongWord;
    Left:          LongInt;
    Top:           LongInt;
    Width:         LongWord;
    Height:        LongWord;
    Style:         LongWord;
    Min:           LongWord;
    Max:           LongWord;
    BackColor:     LongWord;
    ProgressColor: LongWord;
    FrameColor:    LongWord;
  end;

  TFrame = packed record
    Style:        LongWord;
    Width:        Word;
    Left:         SmallInt;
    Height:       Word;
    Top:          SmallInt;
    OuterColor:   LongWord;
    InnerColor:   LongWord;
    Flags:        LongWord;
    Text:         PAnsiChar;
    TextPosition: LongWord;
    Font:         LongWord;
    FontHeight:   LongWord;
    ForeColor:    LongWord;
    BackColor:    LongWord;
  end;

var
  CPUFrame: TFrame = (
    Style: 0;
    Width: 271;
    Left: 8;
    Height: 57;
    Top: 13;
    Flags: FR_ETCHED + FR_CAPTION + FR_FILLED;
    Text: 'Процессора';
    TextPosition: FR_TEXT_POS_TOP;
    Font: 1;
    FontHeight: 16;
  );

  MemFrame: TFrame = (
    Style: 0;
    Width: 271;
    Left: 8;
    Height: 57;
    Top: 84;
    Flags: FR_ETCHED + FR_CAPTION + FR_FILLED;
    Text: 'Памяти';
    TextPosition: FR_TEXT_POS_TOP;
    Font: 1;
    FontHeight: 16;
  );

  CPUProgressBar: TProgressBar = (
    Value: 0;
    Left: 56;
    Top: 37;
    Width: 215;
    Height: 17;
    Style: 0;
    Min: 0;
    Max: 100;
    BackColor: $00FFFFFF;
    ProgressColor: $0000007F;
  );

  MemProgressBar: TProgressBar = (
    Value: 0;
    Left: 56;
    Top: 108;
    Width: 215;
    Height: 17;
    Style: 0;
    Min: 0;
    Max: 100;
    BackColor: $00FFFFFF;
    ProgressColor: $0000007F;
  );

  BoxLib: Pointer;
  progressbar_draw: procedure(var ProgressBar: TProgressBar); stdcall;
  frame_draw:       procedure(var Frame: TFrame); stdcall;

  WndLeft, WndTop: LongInt;
  WndWidth, WndHeight: LongWord;

  SC: TStandardColors;
  CPUMegaHerz: LongWord;

// беззнаковое число Number в строку Buffer, размер буфера > 10
procedure UIntToStr(Number: LongInt; Buffer: PAnsiChar);
asm
         push   edi
         push   esi
         mov    eax, Number
         mov    ecx, 10
         mov    esi, Buffer
         mov    edi, esi
         add    esi, ecx
         mov    byte ptr [esi], 0
@next:
         xor    edx, edx
         div    ecx
         dec    esi
         add    dl, 48
         mov    [esi], dl
         test   eax, eax
         jnz    @next
         mov    ecx, esi
         sub    ecx, edi
         rep    movsb
         pop    esi
         pop    edi
end;

function GetCPUMegaHerz: LongWord;
const
  SLEEP_TIME = 33;

var
  BeginClock, EndClock: Int64;

  function RDTSC: int64;
  asm
    RDTSC
  end;

begin
  BeginClock := RDTSC;
  Sleep(SLEEP_TIME);
  EndClock := RDTSC;
  Result := Int64(EndClock - BeginClock);
  Result := Result div (10000 * SLEEP_TIME);
end;

procedure DrawIndicators;
var
  ValueBuffer: array [0..10] of AnsiChar;
begin
  // определяем значения в процентах
  CPUProgressBar.Value := 100 - (GetIdleTime div 10000) div CPUMegaHerz;
  // "shr 8" чтобы не было переполнения при умножении на 100
  MemProgressBar.Value := 100 - (GetFreeMemory shr 8) * 100 div (GetAvailableMemory shr 8);
  progressbar_draw(CPUProgressBar);
  progressbar_draw(MemProgressBar);
  DrawText(16, CPUProgressBar.Top, '   %', CPUFrame.ForeColor, CPUFrame.BackColor, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);
  UIntToStr(CPUProgressBar.Value, ValueBuffer);
  DrawText(16, CPUProgressBar.Top, ValueBuffer, CPUFrame.ForeColor, CPUFrame.BackColor, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);
  DrawText(16, MemProgressBar.Top, '   %', MemFrame.ForeColor, MemFrame.BackColor, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);
  UIntToStr(MemProgressBar.Value, ValueBuffer);
  DrawText(16, MemProgressBar.Top, ValueBuffer, MemFrame.ForeColor, MemFrame.BackColor, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);
end;

begin
  CPUMegaHerz := GetCPUMegaHerz;
  
  WndHeight := 149 + GetSkinHeight + WINDOW_BORDER_SIZE;
  WndWidth  := 286 + WINDOW_BORDER_SIZE * 2;

  BoxLib           := LoadLibrary('/sys/lib/box_lib.obj');
  frame_draw       := GetProcAddress(BoxLib, 'frame_draw');
  progressbar_draw := GetProcAddress(BoxLib, 'progressbar_draw');

  with GetScreenSize do
  begin
    WndLeft := (Width - WndWidth) div 2;
    WndTop  := (Height - WndHeight) div 2;
  end;

  SetEventMask(EM_REDRAW or EM_BUTTON);
  SetWindowZOrder($FFFFFFFF, ZPOS_ALWAYS_TOP);

  while True do
    case WaitEventByTime(25) of
      REDRAW_EVENT:
        begin
          BeginDraw;
          GetStandardColors(SC, SizeOf(TStandardColors));
          with SC do
          begin
            with CPUFrame do
            begin
              InnerColor := Work3DDark;
              OuterColor := Work3DLight;
              ForeColor  := WorkText;
              BackColor  := Work;
            end;
            with MemFrame do
            begin
              InnerColor := Work3DDark;
              OuterColor := Work3DLight;
              ForeColor  := WorkText;
              BackColor  := Work;
            end;
            CPUProgressBar.FrameColor := WorkGraph;
            MemProgressBar.FrameColor := WorkGraph;
          end;
          WndHeight := 149 + GetSkinHeight + WINDOW_BORDER_SIZE;
          DrawWindow(WndLeft, WndTop, WndWidth, WndHeight, 'Индикатор использования', SC.Work,
            WS_SKINNED_FIXED + WS_CLIENT_COORDS + WS_CAPTION, CAPTION_MOVABLE);
          frame_draw(CPUFrame);
          frame_draw(MemFrame);
          DrawIndicators;
          EndDraw;
        end;
      BUTTON_EVENT:
        Break;
    else
      DrawIndicators;
    end;
end.