1
/********************************************************************
2
KWin - the KDE window manager
3
This file is part of the KDE project.
5
Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>
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.
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
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with this program. If not, see <http://www.gnu.org/licenses/>.
19
*********************************************************************/
23
#include <kwinconfig.h>
25
#include <kconfiggroup.h>
27
#include <ksharedconfig.h>
29
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
30
#include <kwinglutils.h>
32
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
34
#include <X11/extensions/Xrender.h>
37
#include <kwinxrenderutils.h>
46
KWIN_EFFECT(showfps, ShowFpsEffect)
48
const int FPS_WIDTH = 10;
49
const int MAX_TIME = 100;
51
ShowFpsEffect::ShowFpsEffect()
54
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
68
reconfigure(ReconfigureAll);
71
void ShowFpsEffect::reconfigure(ReconfigureFlags)
73
KConfigGroup config(KGlobal::config(), "EffectShowFps");
74
alpha = config.readEntry("Alpha", 0.5);
75
x = config.readEntry("X", -10000);
76
y = config.readEntry("Y", 0);
77
if (x == -10000) // there's no -0 :(
78
x = displayWidth() - 2 * NUM_PAINTS - FPS_WIDTH;
80
x = displayWidth() - 2 * NUM_PAINTS - FPS_WIDTH - x;
82
y = displayHeight() - MAX_TIME;
84
y = displayHeight() - MAX_TIME - y;
85
fps_rect = QRect(x, y, FPS_WIDTH + 2 * NUM_PAINTS, MAX_TIME);
87
config = effects->effectConfig("ShowFps");
88
int textPosition = config.readEntry("TextPosition", int(INSIDE_GRAPH));
89
textFont = config.readEntry("TextFont", QFont());
90
textColor = config.readEntry("TextColor", QColor());
91
double textAlpha = config.readEntry("TextAlpha", 1.0);
93
if (!textColor.isValid())
94
textColor = QPalette().color(QPalette::Active, QPalette::WindowText);
95
textColor.setAlphaF(textAlpha);
97
switch(textPosition) {
99
fpsTextRect = QRect(0, 0, 100, 100);
100
textAlign = Qt::AlignTop | Qt::AlignLeft;
103
fpsTextRect = QRect(displayWidth() - 100, 0, 100, 100);
104
textAlign = Qt::AlignTop | Qt::AlignRight;
107
fpsTextRect = QRect(0, displayHeight() - 100, 100, 100);
108
textAlign = Qt::AlignBottom | Qt::AlignLeft;
111
fpsTextRect = QRect(displayWidth() - 100, displayHeight() - 100, 100, 100);
112
textAlign = Qt::AlignBottom | Qt::AlignRight;
115
fpsTextRect = QRect();
119
fpsTextRect = QRect(x, y, FPS_WIDTH + NUM_PAINTS, MAX_TIME);
120
textAlign = Qt::AlignTop | Qt::AlignRight;
125
void ShowFpsEffect::prePaintScreen(ScreenPrePaintData& data, int time)
128
// TODO optimized away
131
frames[ frames_pos ] = t.minute() * 60000 + t.second() * 1000 + t.msec();
132
if (++frames_pos == MAX_FPS)
134
effects->prePaintScreen(data, time);
135
data.paint += fps_rect;
137
paint_size[ paints_pos ] = 0;
140
void ShowFpsEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data)
142
effects->paintWindow(w, mask, region, data);
144
// Take intersection of region and actual window's rect, minus the fps area
145
// (since we keep repainting it) and count the pixels.
146
QRegion r2 = region & QRect(w->x(), w->y(), w->width(), w->height());
149
foreach (const QRect & r, r2.rects())
150
winsize += r.width() * r.height();
151
paint_size[ paints_pos ] += winsize;
154
void ShowFpsEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data)
156
effects->paintScreen(mask, region, data);
161
if (abs(t.minute() * 60000 + t.second() * 1000 + t.msec() - frames[ i ]) < 1000)
162
++fps; // count all frames in the last second
164
fps = MAX_TIME; // keep it the same height
165
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
166
if (effects->compositingType() == OpenGLCompositing) {
168
glFinish(); // make sure all rendering is done
171
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
172
if (effects->compositingType() == XRenderCompositing) {
174
XSync(display(), False); // make sure all rendering is done
179
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
180
void ShowFpsEffect::paintGL(int fps)
184
#ifndef KWIN_HAVE_OPENGLES
185
glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT);
188
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
189
// TODO painting first the background white and then the contents
190
// means that the contents also blend with the background, I guess
191
if (ShaderManager::instance()->isValid()) {
192
ShaderManager::instance()->pushShader(ShaderManager::ColorShader);
194
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
196
QColor color(255, 255, 255);
197
color.setAlphaF(alpha);
198
vbo->setColor(color);
199
QVector<float> verts;
201
verts << x + 2 * NUM_PAINTS + FPS_WIDTH << y;
203
verts << x << y + MAX_TIME;
204
verts << x << y + MAX_TIME;
205
verts << x + 2 * NUM_PAINTS + FPS_WIDTH << y + MAX_TIME;
206
verts << x + 2 * NUM_PAINTS + FPS_WIDTH << y;
207
vbo->setData(6, 2, verts.constData(), NULL);
208
vbo->render(GL_TRIANGLES);
209
y += MAX_TIME; // paint up from the bottom
212
vbo->setColor(color);
214
verts << x + FPS_WIDTH << y - fps;
215
verts << x << y - fps;
218
verts << x + FPS_WIDTH << y;
219
verts << x + FPS_WIDTH << y - fps;
220
vbo->setData(6, 2, verts.constData(), NULL);
221
vbo->render(GL_TRIANGLES);
225
vbo->setColor(color);
226
QVector<float> vertices;
230
vertices << x << y - i;
231
vertices << x + FPS_WIDTH << y - i;
233
vbo->setData(vertices.size() / 2, 2, vertices.constData(), NULL);
234
vbo->render(GL_LINES);
241
// Paint amount of rendered pixels graph
242
paintDrawSizeGraph(x, y);
243
if (ShaderManager::instance()->isValid()) {
244
ShaderManager::instance()->popShader();
247
// Paint FPS numerical value
252
#ifndef KWIN_HAVE_OPENGLES
258
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
260
Differences between OpenGL and XRender:
261
- differently specified rectangles (X: width/height, O: x2,y2)
262
- XRender uses pre-multiplied alpha
264
void ShowFpsEffect::paintXrender(int fps)
266
Pixmap pixmap = XCreatePixmap(display(), rootWindow(), FPS_WIDTH, MAX_TIME, 32);
267
XRenderPicture p(pixmap, 32);
268
XFreePixmap(display(), pixmap);
270
col.alpha = int(alpha * 0xffff);
271
col.red = int(alpha * 0xffff); // white
272
col.green = int(alpha * 0xffff);
273
col.blue = int(alpha * 0xffff);
274
XRenderFillRectangle(display(), PictOpSrc, p, &col, 0, 0, FPS_WIDTH, MAX_TIME);
277
col.blue = int(alpha * 0xffff);
278
XRenderFillRectangle(display(), PictOpSrc, p, &col, 0, MAX_TIME - fps, FPS_WIDTH, fps);
279
col.red = 0; // black
285
XRenderFillRectangle(display(), PictOpSrc, p, &col, 0, MAX_TIME - i, FPS_WIDTH, 1);
287
XRenderComposite(display(), alpha != 1.0 ? PictOpOver : PictOpSrc, p, None,
288
effects->xrenderBufferPicture(), 0, 0, 0, 0, x, y, FPS_WIDTH, MAX_TIME);
291
paintFPSGraph(x + FPS_WIDTH, y);
293
// Paint amount of rendered pixels graph
294
paintDrawSizeGraph(x + FPS_WIDTH + MAX_TIME, y);
298
void ShowFpsEffect::paintFPSGraph(int x, int y)
302
lines << 10 << 20 << 50;
307
values.append(paints[(i + paints_pos) % NUM_PAINTS ]);
309
paintGraph(x, y, values, lines, true);
312
void ShowFpsEffect::paintDrawSizeGraph(int x, int y)
314
int max_drawsize = 0;
315
for (int i = 0; i < NUM_PAINTS; i++)
316
max_drawsize = qMax(max_drawsize, paint_size[ i ]);
318
// Log of min/max values shown on graph
319
const float max_pixels_log = 7.2f;
320
const float min_pixels_log = 2.0f;
321
const int minh = 5; // Minimum height of the bar when value > 0
323
float drawscale = (MAX_TIME - minh) / (max_pixels_log - min_pixels_log);
324
QList<int> drawlines;
326
for (int logh = (int)min_pixels_log; logh <= max_pixels_log; logh++)
327
drawlines.append((int)((logh - min_pixels_log) * drawscale) + minh);
329
QList<int> drawvalues;
333
int value = paint_size[(i + paints_pos) % NUM_PAINTS ];
336
h = (int)((log10((double)value) - min_pixels_log) * drawscale);
337
h = qMin(qMax(0, h) + minh, MAX_TIME);
339
drawvalues.append(h);
341
paintGraph(x, y, drawvalues, drawlines, false);
344
void ShowFpsEffect::paintGraph(int x, int y, QList<int> values, QList<int> lines, bool colorize)
346
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
347
if (effects->compositingType() == OpenGLCompositing) {
348
QColor color(0, 0, 0);
349
color.setAlphaF(alpha);
350
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
352
vbo->setColor(color);
353
QVector<float> verts;
354
// First draw the lines
355
foreach (int h, lines) {
357
verts << x + values.count() << y - h;
359
vbo->setData(verts.size() / 2, 2, verts.constData(), NULL);
360
vbo->render(GL_LINES);
361
// Then the graph values
364
for (int i = 0; i < values.count(); i++) {
365
int value = values[ i ];
366
if (colorize && value != lastValue) {
367
if (!verts.isEmpty()) {
368
vbo->setData(verts.size() / 2, 2, verts.constData(), NULL);
369
vbo->render(GL_LINES);
373
color = QColor(0, 255, 0);
374
} else if (value <= 20) {
375
color = QColor(255, 255, 0);
376
} else if (value <= 50) {
377
color = QColor(255, 0, 0);
379
color = QColor(0, 0, 0);
381
vbo->setColor(color);
383
verts << x + values.count() - i << y;
384
verts << x + values.count() - i << y - value;
387
if (!verts.isEmpty()) {
388
vbo->setData(verts.size() / 2, 2, verts.constData(), NULL);
389
vbo->render(GL_LINES);
393
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
394
if (effects->compositingType() == XRenderCompositing) {
395
Pixmap pixmap = XCreatePixmap(display(), rootWindow(), values.count(), MAX_TIME, 32);
396
XRenderPicture p(pixmap, 32);
397
XFreePixmap(display(), pixmap);
399
col.alpha = int(alpha * 0xffff);
402
col.red = col.green = col.blue = int(alpha * 0xffff); // white
403
XRenderFillRectangle(display(), PictOpSrc, p, &col, 0, 0, values.count(), MAX_TIME);
406
col.red = col.green = col.blue = int(alpha * 0x8000); // grey
407
for (int i = 0; i < values.count(); i++) {
408
int value = values[ i ];
413
col.green = int(alpha * 0xffff);
415
} else if (value <= 20) {
417
col.red = int(alpha * 0xffff);
418
col.green = int(alpha * 0xffff);
420
} else if (value <= 50) {
422
col.red = int(alpha * 0xffff);
432
XRenderFillRectangle(display(), PictOpSrc, p, &col,
433
values.count() - i, MAX_TIME - value, 1, value);
437
col.red = col.green = col.blue = 0; // black
438
foreach (int h, lines)
439
XRenderFillRectangle(display(), PictOpSrc, p, &col, 0, MAX_TIME - h, values.count(), 1);
441
// Finally render the pixmap onto screen
442
XRenderComposite(display(), alpha != 1.0 ? PictOpOver : PictOpSrc, p, None,
443
effects->xrenderBufferPicture(), 0, 0, 0, 0, x, y, values.count(), MAX_TIME);
448
void ShowFpsEffect::postPaintScreen()
450
effects->postPaintScreen();
451
paints[ paints_pos ] = t.elapsed();
452
if (++paints_pos == NUM_PAINTS)
454
effects->addRepaint(fps_rect);
457
void ShowFpsEffect::paintFPSText(int fps)
459
if (!fpsTextRect.isValid())
461
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
462
QImage im(100, 100, QImage::Format_ARGB32);
464
QPainter painter(&im);
465
painter.setFont(textFont);
466
painter.setPen(textColor);
467
painter.drawText(QRect(0, 0, 100, 100), textAlign, QString::number(fps));
469
fpsText = new GLTexture(im);
471
if (ShaderManager::instance()->isValid()) {
472
GLShader *shader = ShaderManager::instance()->pushShader(ShaderManager::SimpleShader);
473
shader->setUniform("offset", QVector2D(0, 0));
475
fpsText->render(QRegion(fpsTextRect), fpsTextRect);
476
if (ShaderManager::instance()->isValid()) {
477
ShaderManager::instance()->popShader();
480
effects->addRepaint(fpsTextRect);