~ubuntu-branches/ubuntu/wily/lxpanel/wily-proposed

« back to all changes in this revision

Viewing changes to src/plugins/cpu/cpu.c

  • Committer: Package Import Robot
  • Author(s): Julien Lavergne
  • Date: 2015-01-31 15:30:45 UTC
  • mfrom: (1.1.19) (44.1.1 vivid-proposed)
  • Revision ID: package-import@ubuntu.com-20150131153045-iabx5uuxwf2p9sl3
Tags: 0.7.2-1ubuntu1
* Merge with Debian. Ubuntu remaining changes:
* debian/control:
 - Add libindicator-dev build-depends.
 - Add a recommend on xterm | pavucontrol | gnome-alsamixer to enable the
   mixer on the sound applet. (LP: #957749).
 - Add indicator plugin binary.
 - Add build-depends on libicu-dev for weather plugin.
* debian/local/source_lxpanel.py:
 - Add apport hook.
* debian/lxpanel.install:
 - Install all files except indicators.
* debian/lxpanel-indicator-applet-plugin.install:
 - Install indicator plugin.
* debian/rules:
 - Add --enable-indicator-support flag.
 - Add dh_install --fail-missing.
* debian/patches:
 - 04_disable_gtk3_indicators.patch: Hide incompatible indicators in the
   preference menu (LP: #1165245).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/**
2
 
 * CPU usage plugin to lxpanel
3
 
 *
4
 
 * Copyright (c) 2008 LxDE Developers, see the file AUTHORS for details.
5
 
 * Copyright (C) 2004 by Alexandre Pereira da Silva <alexandre.pereira@poli.usp.br>
6
 
 *
7
 
 * This program is free software; you can redistribute it and/or modify
8
 
 * it under the terms of the GNU General Public License as published by
9
 
 * the Free Software Foundation; either version 2 of the License, or
10
 
 * (at your option) any later version.
11
 
 *
12
 
 * This program is distributed in the hope that it will be useful,
13
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
 
 * General Public License for more details.
16
 
 *
17
 
 * You should have received a copy of the GNU General Public License
18
 
 * along with this program; if not, write to the Free Software
19
 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
 
 *
21
 
 */
22
 
/*A little bug fixed by Mykola <mykola@2ka.mipt.ru>:) */
23
 
 
24
 
#include <string.h>
25
 
#include <sys/time.h>
26
 
#include <time.h>
27
 
#include <sys/sysinfo.h>
28
 
#include <stdlib.h>
29
 
#include <glib/gi18n.h>
30
 
 
31
 
#include "plugin.h"
32
 
#include "panel.h"
33
 
#include "misc.h"
34
 
 
35
 
#define BORDER_SIZE 2
36
 
 
37
 
#include "dbg.h"
38
 
 
39
 
typedef unsigned long long CPUTick;             /* Value from /proc/stat */
40
 
typedef float CPUSample;                        /* Saved CPU utilization value as 0.0..1.0 */
41
 
 
42
 
struct cpu_stat {
43
 
    CPUTick u, n, s, i;                         /* User, nice, system, idle */
44
 
};
45
 
 
46
 
/* Private context for CPU plugin. */
47
 
typedef struct {
48
 
    GdkColor foreground_color;                  /* Foreground color for drawing area */
49
 
    GtkWidget * da;                             /* Drawing area */
50
 
    cairo_surface_t * pixmap;                           /* Pixmap to be drawn on drawing area */
51
 
 
52
 
    guint timer;                                /* Timer for periodic update */
53
 
    CPUSample * stats_cpu;                      /* Ring buffer of CPU utilization values */
54
 
    unsigned int ring_cursor;                   /* Cursor for ring buffer */
55
 
    int pixmap_width;                           /* Width of drawing area pixmap; also size of ring buffer; does not include border size */
56
 
    int pixmap_height;                          /* Height of drawing area pixmap; does not include border size */
57
 
    struct cpu_stat previous_cpu_stat;          /* Previous value of cpu_stat */
58
 
} CPUPlugin;
59
 
 
60
 
static void redraw_pixmap(CPUPlugin * c);
61
 
static gboolean cpu_update(CPUPlugin * c);
62
 
static gboolean configure_event(GtkWidget * widget, GdkEventConfigure * event, CPUPlugin * c);
63
 
static gboolean expose_event(GtkWidget * widget, GdkEventExpose * event, CPUPlugin * c);
64
 
static int cpu_constructor(Plugin * p, char ** fp);
65
 
static void cpu_destructor(Plugin * p);
66
 
 
67
 
/* Redraw after timer callback or resize. */
68
 
static void redraw_pixmap(CPUPlugin * c)
69
 
{
70
 
    cairo_t * cr = cairo_create(c->pixmap);
71
 
    cairo_set_line_width (cr, 1.0);
72
 
    /* Erase pixmap. */
73
 
    cairo_rectangle(cr, 0, 0, c->pixmap_width, c->pixmap_height);
74
 
    gdk_cairo_set_source_color(cr, &c->da->style->black);
75
 
    cairo_fill(cr);
76
 
 
77
 
    /* Recompute pixmap. */
78
 
    unsigned int i;
79
 
    unsigned int drawing_cursor = c->ring_cursor;
80
 
    gdk_cairo_set_source_color(cr, &c->foreground_color);
81
 
    for (i = 0; i < c->pixmap_width; i++)
82
 
    {
83
 
        /* Draw one bar of the CPU usage graph. */
84
 
        if (c->stats_cpu[drawing_cursor] != 0.0)
85
 
        {
86
 
            cairo_move_to(cr, i + 0.5, c->pixmap_height);
87
 
            cairo_line_to(cr, i + 0.5, c->pixmap_height - c->stats_cpu[drawing_cursor] * c->pixmap_height);
88
 
            cairo_stroke(cr);
89
 
        }
90
 
 
91
 
        /* Increment and wrap drawing cursor. */
92
 
        drawing_cursor += 1;
93
 
        if (drawing_cursor >= c->pixmap_width)
94
 
            drawing_cursor = 0;
95
 
    }
96
 
 
97
 
    check_cairo_status(cr);
98
 
    cairo_destroy(cr);
99
 
 
100
 
    /* Redraw pixmap. */
101
 
    gtk_widget_queue_draw(c->da);
102
 
}
103
 
 
104
 
/* Periodic timer callback. */
105
 
static gboolean cpu_update(CPUPlugin * c)
106
 
{
107
 
    if ((c->stats_cpu != NULL) && (c->pixmap != NULL))
108
 
    {
109
 
        /* Open statistics file and scan out CPU usage. */
110
 
        struct cpu_stat cpu;
111
 
        FILE * stat = fopen("/proc/stat", "r");
112
 
        if (stat == NULL)
113
 
            return TRUE;
114
 
        int fscanf_result = fscanf(stat, "cpu %llu %llu %llu %llu", &cpu.u, &cpu.n, &cpu.s, &cpu.i);
115
 
        fclose(stat);
116
 
 
117
 
        /* Ensure that fscanf succeeded. */
118
 
        if (fscanf_result == 4)
119
 
        {
120
 
            /* Compute delta from previous statistics. */
121
 
            struct cpu_stat cpu_delta;
122
 
            cpu_delta.u = cpu.u - c->previous_cpu_stat.u;
123
 
            cpu_delta.n = cpu.n - c->previous_cpu_stat.n;
124
 
            cpu_delta.s = cpu.s - c->previous_cpu_stat.s;
125
 
            cpu_delta.i = cpu.i - c->previous_cpu_stat.i;
126
 
 
127
 
            /* Copy current to previous. */
128
 
            memcpy(&c->previous_cpu_stat, &cpu, sizeof(struct cpu_stat));
129
 
 
130
 
            /* Compute user+nice+system as a fraction of total.
131
 
             * Introduce this sample to ring buffer, increment and wrap ring buffer cursor. */
132
 
            float cpu_uns = cpu_delta.u + cpu_delta.n + cpu_delta.s;
133
 
            c->stats_cpu[c->ring_cursor] = cpu_uns / (cpu_uns + cpu_delta.i);
134
 
            c->ring_cursor += 1;
135
 
            if (c->ring_cursor >= c->pixmap_width)
136
 
                c->ring_cursor = 0;
137
 
 
138
 
            /* Redraw with the new sample. */
139
 
            redraw_pixmap(c);
140
 
        }
141
 
    }
142
 
    return TRUE;
143
 
}
144
 
 
145
 
/* Handler for configure_event on drawing area. */
146
 
static gboolean configure_event(GtkWidget * widget, GdkEventConfigure * event, CPUPlugin * c)
147
 
{
148
 
    /* Allocate pixmap and statistics buffer without border pixels. */
149
 
    int new_pixmap_width = widget->allocation.width - BORDER_SIZE * 2;
150
 
    int new_pixmap_height = widget->allocation.height - BORDER_SIZE * 2;
151
 
    if ((new_pixmap_width > 0) && (new_pixmap_height > 0))
152
 
    {
153
 
        /* If statistics buffer does not exist or it changed size, reallocate and preserve existing data. */
154
 
        if ((c->stats_cpu == NULL) || (new_pixmap_width != c->pixmap_width))
155
 
        {
156
 
            CPUSample * new_stats_cpu = g_new0(typeof(*c->stats_cpu), new_pixmap_width);
157
 
            if (c->stats_cpu != NULL)
158
 
            {
159
 
                if (new_pixmap_width > c->pixmap_width)
160
 
                {
161
 
                    /* New allocation is larger.
162
 
                     * Introduce new "oldest" samples of zero following the cursor. */
163
 
                    memcpy(&new_stats_cpu[0],
164
 
                        &c->stats_cpu[0], c->ring_cursor * sizeof(CPUSample));
165
 
                    memcpy(&new_stats_cpu[new_pixmap_width - c->pixmap_width + c->ring_cursor],
166
 
                        &c->stats_cpu[c->ring_cursor], (c->pixmap_width - c->ring_cursor) * sizeof(CPUSample));
167
 
                }
168
 
                else if (c->ring_cursor <= new_pixmap_width)
169
 
                {
170
 
                    /* New allocation is smaller, but still larger than the ring buffer cursor.
171
 
                     * Discard the oldest samples following the cursor. */
172
 
                    memcpy(&new_stats_cpu[0],
173
 
                        &c->stats_cpu[0], c->ring_cursor * sizeof(CPUSample));
174
 
                    memcpy(&new_stats_cpu[c->ring_cursor],
175
 
                        &c->stats_cpu[c->pixmap_width - new_pixmap_width + c->ring_cursor], (new_pixmap_width - c->ring_cursor) * sizeof(CPUSample));
176
 
                }
177
 
                else
178
 
                {
179
 
                    /* New allocation is smaller, and also smaller than the ring buffer cursor.
180
 
                     * Discard all oldest samples following the ring buffer cursor and additional samples at the beginning of the buffer. */
181
 
                    memcpy(&new_stats_cpu[0],
182
 
                        &c->stats_cpu[c->ring_cursor - new_pixmap_width], new_pixmap_width * sizeof(CPUSample));
183
 
                    c->ring_cursor = 0;
184
 
                }
185
 
                g_free(c->stats_cpu);
186
 
            }
187
 
            c->stats_cpu = new_stats_cpu;
188
 
        }
189
 
 
190
 
        /* Allocate or reallocate pixmap. */
191
 
        c->pixmap_width = new_pixmap_width;
192
 
        c->pixmap_height = new_pixmap_height;
193
 
        if (c->pixmap)
194
 
            cairo_surface_destroy(c->pixmap);
195
 
        c->pixmap = cairo_image_surface_create(CAIRO_FORMAT_RGB24, c->pixmap_width, c->pixmap_height);
196
 
        check_cairo_surface_status(&c->pixmap);
197
 
 
198
 
        /* Redraw pixmap at the new size. */
199
 
        redraw_pixmap(c);
200
 
    }
201
 
    return TRUE;
202
 
}
203
 
 
204
 
/* Handler for expose_event on drawing area. */
205
 
static gboolean expose_event(GtkWidget * widget, GdkEventExpose * event, CPUPlugin * c)
206
 
{
207
 
    /* Draw the requested part of the pixmap onto the drawing area.
208
 
     * Translate it in both x and y by the border size. */
209
 
    if (c->pixmap != NULL)
210
 
    {
211
 
        cairo_t * cr = gdk_cairo_create(widget->window);
212
 
        gdk_cairo_region(cr, event->region);
213
 
        cairo_clip(cr);
214
 
        gdk_cairo_set_source_color(cr, &c->da->style->black);
215
 
        cairo_set_source_surface(cr, c->pixmap,
216
 
              BORDER_SIZE, BORDER_SIZE);
217
 
        cairo_paint(cr);
218
 
        check_cairo_status(cr);
219
 
        cairo_destroy(cr);
220
 
    }
221
 
    return FALSE;
222
 
}
223
 
 
224
 
/* Plugin constructor. */
225
 
static int cpu_constructor(Plugin * p, char ** fp)
226
 
{
227
 
    /* Allocate plugin context and set into Plugin private data pointer. */
228
 
    CPUPlugin * c = g_new0(CPUPlugin, 1);
229
 
    p->priv = c;
230
 
 
231
 
    /* Allocate top level widget and set into Plugin widget pointer. */
232
 
    p->pwid = gtk_event_box_new();
233
 
    gtk_container_set_border_width(GTK_CONTAINER(p->pwid), 1);
234
 
    GTK_WIDGET_SET_FLAGS(p->pwid, GTK_NO_WINDOW);
235
 
 
236
 
    /* Allocate drawing area as a child of top level widget.  Enable button press events. */
237
 
    c->da = gtk_drawing_area_new();
238
 
    gtk_widget_set_size_request(c->da, 40, PANEL_HEIGHT_DEFAULT);
239
 
    gtk_widget_add_events(c->da, GDK_BUTTON_PRESS_MASK);
240
 
    gtk_container_add(GTK_CONTAINER(p->pwid), c->da);
241
 
 
242
 
    /* Clone a graphics context and set "green" as its foreground color.
243
 
     * We will use this to draw the graph. */
244
 
    gdk_color_parse("green",  &c->foreground_color);
245
 
 
246
 
    /* Connect signals. */
247
 
    g_signal_connect(G_OBJECT(c->da), "configure_event", G_CALLBACK(configure_event), (gpointer) c);
248
 
    g_signal_connect(G_OBJECT(c->da), "expose_event", G_CALLBACK(expose_event), (gpointer) c);
249
 
    g_signal_connect(c->da, "button-press-event", G_CALLBACK(plugin_button_press_event), p);
250
 
 
251
 
    /* Show the widget.  Connect a timer to refresh the statistics. */
252
 
    gtk_widget_show(c->da);
253
 
    c->timer = g_timeout_add(1500, (GSourceFunc) cpu_update, (gpointer) c);
254
 
    return 1;
255
 
}
256
 
 
257
 
/* Plugin destructor. */
258
 
static void cpu_destructor(Plugin * p)
259
 
{
260
 
    CPUPlugin * c = (CPUPlugin *) p->priv;
261
 
 
262
 
    /* Disconnect the timer. */
263
 
    g_source_remove(c->timer);
264
 
 
265
 
    /* Deallocate memory. */
266
 
    cairo_surface_destroy(c->pixmap);
267
 
    g_free(c->stats_cpu);
268
 
    g_free(c);
269
 
}
270
 
 
271
 
/* Plugin descriptor. */
272
 
PluginClass cpu_plugin_class = {
273
 
 
274
 
    PLUGINCLASS_VERSIONING,
275
 
 
276
 
    type : "cpu",
277
 
    name : N_("CPU Usage Monitor"),
278
 
    version: "1.0",
279
 
    description : N_("Display CPU usage"),
280
 
 
281
 
    constructor : cpu_constructor,
282
 
    destructor  : cpu_destructor,
283
 
    config : NULL,
284
 
    save : NULL
285
 
};