mirror of
https://github.com/edeproject/ede.git
synced 2023-08-10 21:13:03 +03:00
Importing adapted CPU monitor from old eworkpanel as applet
This commit is contained in:
parent
75aca1e984
commit
c4378d8738
@ -376,6 +376,7 @@ void Panel::load_applets(void) {
|
||||
"clock.so",
|
||||
"taskbar.so",
|
||||
"keyboard_layout.so",
|
||||
"cpu_monitor.so",
|
||||
0
|
||||
};
|
||||
|
||||
@ -398,6 +399,7 @@ void Panel::load_applets(void) {
|
||||
mgr.load("./applets/clock/clock.so");
|
||||
mgr.load("./applets/taskbar/taskbar.so");
|
||||
mgr.load("./applets/keyboard-layout/keyboard_layout.so");
|
||||
mgr.load("./applets/cpu-monitor/cpu_monitor.so");
|
||||
mgr.fill_group(this);
|
||||
#endif
|
||||
}
|
||||
|
@ -12,8 +12,9 @@ SubDir TOP ede-panel applets ;
|
||||
|
||||
SubInclude TOP ede-panel applets demo ;
|
||||
SubInclude TOP ede-panel applets clock ;
|
||||
SubInclude TOP ede-panel applets cpu-monitor ;
|
||||
SubInclude TOP ede-panel applets keyboard-layout ;
|
||||
SubInclude TOP ede-panel applets pager ;
|
||||
SubInclude TOP ede-panel applets quick-launch ;
|
||||
SubInclude TOP ede-panel applets start-menu ;
|
||||
SubInclude TOP ede-panel applets taskbar ;
|
||||
SubInclude TOP ede-panel applets keyboard-layout ;
|
||||
|
500
ede-panel/applets/cpu-monitor/CpuMonitor.cpp
Normal file
500
ede-panel/applets/cpu-monitor/CpuMonitor.cpp
Normal file
@ -0,0 +1,500 @@
|
||||
/*
|
||||
* IceWM
|
||||
*
|
||||
* Copyright (C) 1998-2001 Marko Macek
|
||||
*
|
||||
* CPU Status
|
||||
*
|
||||
* For eWorkPanel by Mikko Lahteenmaki 2003
|
||||
* For ede-panel by Sanel Zukan 2009
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <FL/Fl.H>
|
||||
#include <FL/Fl_Group.H>
|
||||
#include <FL/fl_draw.H>
|
||||
#include <edelib/Nls.h>
|
||||
|
||||
#if defined(linux)
|
||||
# include <sys/sysinfo.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_KSTAT_H
|
||||
# include <kstat.h>
|
||||
#endif
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
# include <sys/param.h>
|
||||
# include <sys/sysctl.h>
|
||||
# if __FreeBSD_version < 500101
|
||||
# include <sys/dkstat.h>
|
||||
# else
|
||||
# include <sys/resource.h>
|
||||
# endif
|
||||
# include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#include "Applet.h"
|
||||
#include "CpuMonitor.h"
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
|
||||
/* The part ripped from top... */
|
||||
/*
|
||||
* Top users/processes display for Unix
|
||||
* Version 3
|
||||
*
|
||||
* This program may be freely redistributed,
|
||||
* but this entire comment MUST remain intact.
|
||||
*
|
||||
* Copyright (c) 1984, 1989, William LeFebvre, Rice University
|
||||
* Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
|
||||
*/
|
||||
|
||||
/*
|
||||
* percentages(cnt, out, new, old, diffs) - calculate percentage change
|
||||
* between array "old" and "new", putting the percentages i "out".
|
||||
* "cnt" is size of each array and "diffs" is used for scratch space.
|
||||
* The array "old" is updated on each call.
|
||||
* The routine assumes modulo arithmetic. This function is especially
|
||||
* useful on BSD mchines for calculating cpu state percentages.
|
||||
*/
|
||||
|
||||
static long cp_time[CPUSTATES];
|
||||
static long cp_old[CPUSTATES];
|
||||
static long cp_diff[CPUSTATES];
|
||||
static int cpu_states[CPUSTATES];
|
||||
|
||||
long percentages(int cnt, int *out, long *my_new, long *old, long *diffs)
|
||||
{
|
||||
register int i;
|
||||
register long change;
|
||||
register long total_change;
|
||||
register long *dp;
|
||||
long half_total;
|
||||
|
||||
/* initialization */
|
||||
total_change = 0;
|
||||
dp = diffs;
|
||||
|
||||
/* calculate changes for each state and the overall change */
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
if ((change = *my_new - *old) < 0)
|
||||
{
|
||||
/* this only happens when the counter wraps */
|
||||
change = (int)
|
||||
((unsigned long)*my_new-(unsigned long)*old);
|
||||
}
|
||||
total_change += (*dp++ = change);
|
||||
*old++ = *my_new++;
|
||||
}
|
||||
|
||||
/* avoid divide by zero potential */
|
||||
if (total_change == 0)
|
||||
{
|
||||
total_change = 1;
|
||||
}
|
||||
|
||||
/* calculate percentages based on overall change, rounding up */
|
||||
half_total = total_change / 2l;
|
||||
|
||||
/* Do not divide by 0. Causes Floating point exception */
|
||||
if(total_change) {
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
*out++ = (int)((*diffs++ * 1000 + half_total) / total_change);
|
||||
}
|
||||
}
|
||||
|
||||
/* return the total in case the caller wants to use it */
|
||||
return total_change;
|
||||
}
|
||||
|
||||
#endif /* freebsd */
|
||||
|
||||
|
||||
#define UPDATE_INTERVAL .5f
|
||||
|
||||
void cpu_timeout_cb(void *d) {
|
||||
((CPUMonitor*)d)->update_status();
|
||||
Fl::repeat_timeout(UPDATE_INTERVAL, cpu_timeout_cb, d);
|
||||
}
|
||||
|
||||
CPUMonitor::CPUMonitor() : Fl_Box(0, 0, 45, 25)
|
||||
{
|
||||
box(FL_THIN_DOWN_BOX);
|
||||
|
||||
m_draw_label = true;
|
||||
m_samples = m_old_samples = -1;
|
||||
cpu = 0;
|
||||
|
||||
colors[IWM_USER] = FL_RED;
|
||||
colors[IWM_NICE] = FL_GREEN;
|
||||
colors[IWM_SYS] = FL_DARK3;
|
||||
colors[IWM_IDLE] = FL_BACKGROUND_COLOR;
|
||||
|
||||
layout();
|
||||
Fl::add_timeout(UPDATE_INTERVAL, cpu_timeout_cb, this);
|
||||
}
|
||||
|
||||
void CPUMonitor::clear()
|
||||
{
|
||||
if(!cpu) return;
|
||||
|
||||
for (int i = 0; i < samples(); i++)
|
||||
delete cpu[i];
|
||||
|
||||
delete cpu;
|
||||
cpu = 0;
|
||||
|
||||
m_old_samples = -1;
|
||||
}
|
||||
|
||||
void CPUMonitor::draw()
|
||||
{
|
||||
draw_box();
|
||||
|
||||
if(!cpu && label()) {
|
||||
draw_label();
|
||||
return;
|
||||
}
|
||||
|
||||
int n, c, user, nice, sys, idle, total;
|
||||
int W = w() - Fl::box_dw(box());
|
||||
int H = h() - Fl::box_dh(box());
|
||||
int X = x() + Fl::box_dx(box());
|
||||
int Y = y() + Fl::box_dy(box());
|
||||
|
||||
fl_push_clip(X, Y, W, H);
|
||||
|
||||
c = 0;
|
||||
for (int i = X; i < samples() + X; i++) {
|
||||
user = cpu[c][IWM_USER];
|
||||
nice = cpu[c][IWM_NICE];
|
||||
sys = cpu[c][IWM_SYS];
|
||||
idle = cpu[c][IWM_IDLE];
|
||||
|
||||
total = user + sys + nice + idle;
|
||||
c++;
|
||||
|
||||
int y = Y + H;
|
||||
|
||||
if (total > 0) {
|
||||
if (sys) {
|
||||
n = (H * (total - sys)) / total; // check rounding
|
||||
if (n >= y) n = y;
|
||||
if (n < 1) n = 1;
|
||||
fl_color(colors[IWM_SYS]);
|
||||
fl_line(i, y, i, n);
|
||||
y = n - 1;
|
||||
}
|
||||
|
||||
if (nice) {
|
||||
n = (H * (total - sys - nice)) / total;
|
||||
if (n >= y) n = y;
|
||||
if (n < 1) n = 1;
|
||||
fl_color(colors[IWM_NICE]);
|
||||
fl_line(i, y, i, n);
|
||||
y = n - 1;
|
||||
}
|
||||
|
||||
if (user) {
|
||||
n = (H * (total - sys - nice - user)) / total;
|
||||
if (n >= y) n = y;
|
||||
if (n < 1) n = 1;
|
||||
fl_color(colors[IWM_USER]);
|
||||
fl_line(i, y, i, n);
|
||||
y = n - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (idle) {
|
||||
if(colors[IWM_IDLE] != FL_BACKGROUND_COLOR) {
|
||||
fl_color(colors[IWM_IDLE]);
|
||||
fl_line(i, Fl::box_dy(box()), i, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
draw_label();
|
||||
fl_pop_clip();
|
||||
}
|
||||
|
||||
void CPUMonitor::layout() {
|
||||
m_samples = w() - Fl::box_dw(box());
|
||||
|
||||
if(!cpu || m_old_samples != m_samples) {
|
||||
clear();
|
||||
|
||||
cpu = new int*[m_samples];
|
||||
for(int i=0; i < m_samples; i++) {
|
||||
cpu[i] = new int[IWM_STATES];
|
||||
cpu[i][IWM_USER] = cpu[i][IWM_NICE] = cpu[i][IWM_SYS] = 0;
|
||||
cpu[i][IWM_IDLE] = 1;
|
||||
}
|
||||
last_cpu[IWM_USER] = last_cpu[IWM_NICE] = last_cpu[IWM_SYS] = last_cpu[IWM_IDLE] = 0;
|
||||
|
||||
update_status();
|
||||
m_old_samples = m_samples;
|
||||
}
|
||||
}
|
||||
|
||||
void CPUMonitor::update_status() {
|
||||
if(!cpu) return;
|
||||
|
||||
for (int i=1; i < samples(); i++) {
|
||||
cpu[i - 1][IWM_USER] = cpu[i][IWM_USER];
|
||||
cpu[i - 1][IWM_NICE] = cpu[i][IWM_NICE];
|
||||
cpu[i - 1][IWM_SYS] = cpu[i][IWM_SYS];
|
||||
cpu[i - 1][IWM_IDLE] = cpu[i][IWM_IDLE];
|
||||
}
|
||||
|
||||
get_cpu_info();
|
||||
|
||||
// Update tooltip
|
||||
static char load[255];
|
||||
snprintf(load, sizeof(load)-1,
|
||||
_("CPU Load:\n"
|
||||
"User: %d%%\n"
|
||||
"Nice: %d%%\n"
|
||||
"Sys: %d%%\n"
|
||||
"Idle: %d%%"),
|
||||
cpu[samples()-1][0]*2, cpu[samples()-1][1]*2,
|
||||
cpu[samples()-1][2]*2, cpu[samples()-1][3]*2);
|
||||
|
||||
// Update label
|
||||
int cpu_percent = cpu[samples()-1][0]*2;
|
||||
if(m_draw_label && cpu_percent<=100) {
|
||||
static char buf[16];
|
||||
snprintf(buf, sizeof(buf), "%i%%", cpu_percent);
|
||||
label(buf);
|
||||
}
|
||||
|
||||
tooltip(load);
|
||||
}
|
||||
|
||||
void CPUMonitor::get_cpu_info() {
|
||||
if(!cpu) return;
|
||||
|
||||
#ifdef linux
|
||||
char *p, buf[128];
|
||||
long cur[IWM_STATES];
|
||||
int len, fd = open("/proc/stat", O_RDONLY);
|
||||
|
||||
cpu[samples()-1][IWM_USER] = 0;
|
||||
cpu[samples()-1][IWM_NICE] = 0;
|
||||
cpu[samples()-1][IWM_SYS] = 0;
|
||||
cpu[samples()-1][IWM_IDLE] = 0;
|
||||
|
||||
if (fd == -1)
|
||||
return;
|
||||
len = read(fd, buf, sizeof(buf) - 1);
|
||||
if (len != sizeof(buf) - 1) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
buf[len] = 0;
|
||||
|
||||
p = buf;
|
||||
while (*p && (*p < '0' || *p > '9'))
|
||||
p++;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
cur[i] = strtoul(p, &p, 10);
|
||||
cpu[samples()-1][i] = cur[i] - last_cpu[i];
|
||||
last_cpu[i] = cur[i];
|
||||
}
|
||||
close(fd);
|
||||
#if 0
|
||||
fprintf(stderr, "cpu: %d %d %d %d\n",
|
||||
cpu[samples()-1][IWM_USER], cpu[samples()-1][IWM_NICE],
|
||||
cpu[samples()-1][IWM_SYS], cpu[samples()-1][IWM_IDLE]);
|
||||
#endif
|
||||
|
||||
#endif /* linux */
|
||||
|
||||
#ifdef HAVE_KSTAT_H
|
||||
# ifdef HAVE_OLD_KSTAT
|
||||
# define ui32 ul
|
||||
#endif
|
||||
|
||||
static kstat_ctl_t *kc = NULL;
|
||||
static kid_t kcid;
|
||||
kid_t new_kcid;
|
||||
kstat_t *ks = NULL;
|
||||
kstat_named_t *kn = NULL;
|
||||
int changed,change,total_change;
|
||||
unsigned int thiscpu;
|
||||
register int i,j;
|
||||
static unsigned int ncpus;
|
||||
static kstat_t **cpu_ks=NULL;
|
||||
static cpu_stat_t *cpu_stat=NULL;
|
||||
static long cp_old[CPU_STATES];
|
||||
long cp_time[CPU_STATES], cp_pct[CPU_STATES];
|
||||
|
||||
/* Initialize the kstat */
|
||||
if (!kc) {
|
||||
kc = kstat_open();
|
||||
if (!kc) {
|
||||
perror("kstat_open ");
|
||||
return;/* FIXME : need err handler? */
|
||||
}
|
||||
changed = 1;
|
||||
kcid = kc->kc_chain_id;
|
||||
fcntl(kc->kc_kd, F_SETFD, FD_CLOEXEC);
|
||||
} else {
|
||||
changed = 0;
|
||||
}
|
||||
/* Fetch the kstat data. Whenever we detect that the kstat has been
|
||||
changed by the kernel, we 'continue' and restart this loop.
|
||||
Otherwise, we break out at the end. */
|
||||
while (1) {
|
||||
new_kcid = kstat_chain_update(kc);
|
||||
if (new_kcid) {
|
||||
changed = 1;
|
||||
kcid = new_kcid;
|
||||
}
|
||||
if (new_kcid < 0) {
|
||||
perror("kstat_chain_update ");
|
||||
return;/* FIXME : need err handler? */
|
||||
}
|
||||
if (new_kcid != 0)
|
||||
continue; /* kstat changed - start over */
|
||||
ks = kstat_lookup(kc, "unix", 0, "system_misc");
|
||||
if (kstat_read(kc, ks, 0) == -1) {
|
||||
perror("kstat_read ");
|
||||
return;/* FIXME : need err handler? */
|
||||
}
|
||||
if (changed) {
|
||||
/* the kstat has changed - reread the data */
|
||||
thiscpu = 0; ncpus = 0;
|
||||
kn = (kstat_named_t *)kstat_data_lookup(ks, "ncpus");
|
||||
if ((kn) && (kn->value.ui32 > ncpus)) {
|
||||
/* I guess I should be using 'new' here... FIXME */
|
||||
ncpus = kn->value.ui32;
|
||||
if ((cpu_ks = (kstat_t **)
|
||||
realloc(cpu_ks, ncpus * sizeof(kstat_t *))) == NULL)
|
||||
{
|
||||
perror("realloc: cpu_ks ");
|
||||
return;/* FIXME : need err handler? */
|
||||
}
|
||||
if ((cpu_stat = (cpu_stat_t *)
|
||||
realloc(cpu_stat, ncpus * sizeof(cpu_stat_t))) == NULL)
|
||||
{
|
||||
perror("realloc: cpu_stat ");
|
||||
return;/* FIXME : need err handler? */
|
||||
}
|
||||
}
|
||||
for (ks = kc->kc_chain; ks; ks = ks->ks_next) {
|
||||
if (strncmp(ks->ks_name, "cpu_stat", 8) == 0) {
|
||||
new_kcid = kstat_read(kc, ks, NULL);
|
||||
if (new_kcid < 0) {
|
||||
perror("kstat_read ");
|
||||
return;/* FIXME : need err handler? */
|
||||
}
|
||||
if (new_kcid != kcid)
|
||||
break;
|
||||
cpu_ks[thiscpu] = ks;
|
||||
thiscpu++;
|
||||
if (thiscpu > ncpus) {
|
||||
fprintf(stderr, "kstat finds too many cpus: should be %d", ncpus);
|
||||
return;/* FIXME : need err handler? */
|
||||
}
|
||||
}
|
||||
}
|
||||
if (new_kcid != kcid)
|
||||
continue;
|
||||
ncpus = thiscpu;
|
||||
changed = 0;
|
||||
}
|
||||
for (i = 0; i<(int)ncpus; i++) {
|
||||
new_kcid = kstat_read(kc, cpu_ks[i], &cpu_stat[i]);
|
||||
if (new_kcid < 0) {
|
||||
perror("kstat_read ");
|
||||
return;/* FIXME : need err handler? */
|
||||
}
|
||||
}
|
||||
if (new_kcid != kcid)
|
||||
continue; /* kstat changed - start over */
|
||||
else
|
||||
break;
|
||||
} /* while (1) */
|
||||
|
||||
/* Initialize the cp_time array */
|
||||
for (i = 0; i < CPU_STATES; i++)
|
||||
cp_time[i] = 0L;
|
||||
for (i = 0; i < (int)ncpus; i++) {
|
||||
for (j = 0; j < CPU_STATES; j++)
|
||||
cp_time[j] += (long) cpu_stat[i].cpu_sysinfo.cpu[j];
|
||||
}
|
||||
/* calculate the percent utilization for each category */
|
||||
/* cpu_state calculations */
|
||||
total_change = 0;
|
||||
for (i = 0; i < CPU_STATES; i++) {
|
||||
change = cp_time[i] - cp_old[i];
|
||||
if (change < 0) /* The counter rolled over */
|
||||
change = (int) ((unsigned long)cp_time[i] - (unsigned long)cp_old[i]);
|
||||
cp_pct[i] = change;
|
||||
total_change += change;
|
||||
cp_old[i] = cp_time[i]; /* copy the data for the next run */
|
||||
}
|
||||
/* this percent calculation isn't really needed, since the repaint
|
||||
routine takes care of this... */
|
||||
for (i = 0; i < CPU_STATES; i++)
|
||||
cp_pct[i] =
|
||||
((total_change > 0) ?
|
||||
((int)(((1000.0 * (float)cp_pct[i]) / total_change) + 0.5)) :
|
||||
((i == CPU_IDLE) ? (1000) : (0)));
|
||||
|
||||
/* OK, we've got the data. Now copy it to cpu[][] */
|
||||
cpu[samples()-1][IWM_USER] = cp_pct[CPU_USER];
|
||||
cpu[samples()-1][IWM_NICE] = cp_pct[CPU_WAIT];
|
||||
cpu[samples()-1][IWM_SYS] = cp_pct[CPU_KERNEL];
|
||||
cpu[samples()-1][IWM_IDLE] = cp_pct[CPU_IDLE];
|
||||
|
||||
#endif /* have_kstat_h */
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
size_t len = sizeof(cp_time);
|
||||
|
||||
cpu[samples()-1][IWM_USER] = 0;
|
||||
cpu[samples()-1][IWM_NICE] = 0;
|
||||
cpu[samples()-1][IWM_SYS] = 0;
|
||||
cpu[samples()-1][IWM_IDLE] = 0;
|
||||
|
||||
if (sysctlbyname("kern.cp_time", &cp_time, &len, NULL, 0) == -1)
|
||||
return; /* FIXME : need err handler? */
|
||||
|
||||
percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
|
||||
|
||||
// Translate FreeBSD stuff into ours (probably the same thing anyway)
|
||||
cpu[samples()-1][IWM_USER] = cp_diff[CP_USER];
|
||||
cpu[samples()-1][IWM_NICE] = cp_diff[CP_NICE];
|
||||
cpu[samples()-1][IWM_SYS] = cp_diff[CP_SYS];
|
||||
cpu[samples()-1][IWM_IDLE] = cp_diff[CP_IDLE];
|
||||
|
||||
#if 0
|
||||
fprintf(stderr, "cpu: %d %d %d %d\n",
|
||||
cpu[samples()-1][IWM_USER], cpu[samples()-1][IWM_NICE],
|
||||
cpu[samples()-1][IWM_SYS], cpu[samples()-1][IWM_IDLE]);
|
||||
#endif
|
||||
|
||||
#endif /* freebsd */
|
||||
}
|
||||
|
||||
EDE_PANEL_APPLET_EXPORT (
|
||||
CPUMonitor,
|
||||
EDE_PANEL_APPLET_OPTION_ALIGN_RIGHT,
|
||||
"CPU monitor",
|
||||
"0.1",
|
||||
"empty",
|
||||
"various"
|
||||
)
|
46
ede-panel/applets/cpu-monitor/CpuMonitor.h
Normal file
46
ede-panel/applets/cpu-monitor/CpuMonitor.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef __CPUMONITOR_H__
|
||||
#define __CPUMONITOR_H__
|
||||
|
||||
#include <FL/Fl_Box.H>
|
||||
|
||||
/*
|
||||
#ifdef HAVE_KSTAT_H
|
||||
# include <kstat.h>
|
||||
# include <sys/sysinfo.h>
|
||||
#endif
|
||||
*/
|
||||
|
||||
enum {
|
||||
IWM_USER = 0,
|
||||
IWM_NICE,
|
||||
IWM_SYS,
|
||||
IWM_IDLE,
|
||||
IWM_STATES
|
||||
};
|
||||
|
||||
class CPUMonitor : public Fl_Box {
|
||||
private:
|
||||
bool m_draw_label;
|
||||
int m_old_samples;
|
||||
int m_samples;
|
||||
|
||||
int **cpu;
|
||||
long last_cpu[IWM_STATES];
|
||||
Fl_Color colors[IWM_STATES];
|
||||
|
||||
public:
|
||||
CPUMonitor();
|
||||
~CPUMonitor() { clear(); }
|
||||
|
||||
void clear();
|
||||
|
||||
void update_status();
|
||||
void get_cpu_info();
|
||||
|
||||
void draw();
|
||||
void layout();
|
||||
|
||||
int samples() const { return m_samples; }
|
||||
};
|
||||
|
||||
#endif
|
13
ede-panel/applets/cpu-monitor/Jamfile
Normal file
13
ede-panel/applets/cpu-monitor/Jamfile
Normal file
@ -0,0 +1,13 @@
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# Part of Equinox Desktop Environment (EDE).
|
||||
# Copyright (c) 2009 EDE Authors.
|
||||
#
|
||||
# This program is licensed under terms of the
|
||||
# GNU General Public License version 2 or newer.
|
||||
# See COPYING for details.
|
||||
|
||||
SubDir TOP ede-panel applets cpu-monitor ;
|
||||
|
||||
PanelApplet cpu_monitor : CpuMonitor.cpp ;
|
Loading…
Reference in New Issue
Block a user