Merge lp:~mc-return/compiz/compiz.merge-fix771875-screenshot-overlay-on-screenshots-with-enabled-fbos into lp:compiz/0.9.10

Proposed by MC Return
Status: Merged
Approved by: Sam Spilsbury
Approved revision: 3669
Merged at revision: 3679
Proposed branch: lp:~mc-return/compiz/compiz.merge-fix771875-screenshot-overlay-on-screenshots-with-enabled-fbos
Merge into: lp:compiz/0.9.10
Diff against target: 536 lines (+318/-145)
2 files modified
plugins/opengl/include/opengl/opengl.h (+4/-0)
plugins/screenshot/src/screenshot.cpp (+314/-145)
To merge this branch: bzr merge lp:~mc-return/compiz/compiz.merge-fix771875-screenshot-overlay-on-screenshots-with-enabled-fbos
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Sam Spilsbury Approve
MC Return Needs Information
Review via email: mp+160751@code.launchpad.net

Commit message

Refactor screenshot code to make it clearer and also to allow us to take
screenshots in glPaintOutput as opposed to paint ().

Taking screenshots in paint () was probably correct pre-GLES, but isn't
really correct now - we want to be able to read off of the currently
bound scratch framebuffer where we last painted the frame. Reading off
the frontbuffer is an imprecise operation because the contents of both
buffers are really undefined after a swap. In the case where we are
not painting to a scratch framebuffer object, we'll just end up reading
from the backbuffer anyways, which would be correct to do mid-frame.

Also added the new static const GLenums
DRAW_FRAMEBUFFER_BINDING and
READ_FRAMEBUFFER_BINDING to opengl.h.

(LP: #771875)

Description of the change

\o/

I declare Screenshot to be 100% ready now.

@Sam: Thanks 4 the tip with glReadBuffer. The GL_FRONT version did the
trick. :) &&
thanks for the additional refactoring :)

To post a comment you must log in.
Revision history for this message
Sam Spilsbury (smspillaz) wrote :

I don't think reading off the front buffer is correct. It should actually be the back buffer.

In any case I'm a little hesitant to ack this right now as I mentioned earlier. There are other plugins (namley unity) that assume that the current read buffer binding hasn't been touched. The only way they'd be able to know whether or not the binding should be switched is by querying OpenGL directly, and such queries are usually quite slow.

I'd much rather have a framework to track changes in the buffer bindings, a-la something like this: https://code.launchpad.net/~compiz-team/compiz/compiz.fix_1040478/+merge/156465 before we do anything here.

So - we'll get back to this as soon as the right frameworks are in place to enable it properly. Sorry about that :/

Revision history for this message
MC Return (mc-return) wrote :

> I don't think reading off the front buffer is correct. It should actually be
> the back buffer.
>

Of course I tried the back buffer version first -> Did not work.
I also tried to flush, clear, clearcolor, forcing transparency to 1.0, but
nothing worked, if FBOs are enabled... :(

> In any case I'm a little hesitant to ack this right now as I mentioned
> earlier. There are other plugins (namley unity) that assume that the current
> read buffer binding hasn't been touched. The only way they'd be able to know
> whether or not the binding should be switched is by querying OpenGL directly,
> and such queries are usually quite slow.
>

I did try to optimize behaviour by just changing the buffer to front if FBOs are
enabled.
Would it help to set it to back buffer again, after glReadPixels has finished ?

> I'd much rather have a framework to track changes in the buffer bindings, a-la
> something like this: https://code.launchpad.net/~compiz-
> team/compiz/compiz.fix_1040478/+merge/156465 before we do anything here.
>

Okay, no problem -> this can wait. Folks now already have the ability to set the
opacity of the screenshot overlay manually, so they can workaround this nasty
bug in trunk version of Screenshot already.
So no need for any hurry here...

> So - we'll get back to this as soon as the right frameworks are in place to
> enable it properly. Sorry about that :/

No problem at all. This is just the point of the i.

Revision history for this message
MC Return (mc-return) wrote :

Set to WIP.

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

Cheers. Will get on to this as soon as it makes sense to do so.

On Thu, Apr 25, 2013 at 3:53 PM, MC Return <email address hidden> wrote:
> Set to WIP.
> --
> https://code.launchpad.net/~mc-return/compiz/compiz.merge-fix771875-screenshot-overlay-on-screenshots-with-enabled-fbos/+merge/160751
> Your team Compiz Maintainers is requested to review the proposed merge of lp:~mc-return/compiz/compiz.merge-fix771875-screenshot-overlay-on-screenshots-with-enabled-fbos into lp:compiz.

--
Sam Spilsbury

Revision history for this message
MC Return (mc-return) wrote :

> Cheers. Will get on to this as soon as it makes sense to do so.
>
+1. No stress with that one. Every user can get it to work correctly already.

Revision history for this message
MC Return (mc-return) wrote :

Sam, just 2 questions regarding this matter:

1. Are you aware that glReadBuffer (GL_FRONT); will just be called once per screenshot and just in the case FBOs are enabled, that means if the user is veryveryvery fast, maximally 3 times per second ?

2. Would it help to call glReadBuffer (GL_BACK); after glReadPixels (...) if (GL::fboEnabled) to change it back, or is
this a stupid and useless idea ?

review: Needs Information
Revision history for this message
Sam Spilsbury (smspillaz) wrote :

Actually, I think you're probably right on this one. Though I think if
I remember correctly now the right approach is actually to read off of
the currently bound framebuffer object and not the frontbuffer. The
reason why the overlay persists is because the backbuffer is always
one-frame-behind. I'm not really sure why there is no overlay on the
frontbuffer, perhaps it is a race condition. In any case, those pixels
will be at least one frame out of date.

The way screenshot works is that it paints the overlay, and then upon
"deciding" to take the screenshot, damages that region and then calls
glReadPixels on the next paint pass. The pixels for that frame will be
painted into the currently bound framebuffer object.

So the correct approach would be:

GLuint readBufferBinding = 0;

if (GL::fboEnabled)
{
    glGetIntegerv (GL_READ_FRAMEBUFFER, &readBufferBinding);
    /* We want to read off of the last frame, so bind the backbuffer */
    GL::bindFramebuffer (GL_READ_FRAMEBUFFER, 0);
}

...

glReadPixels

...

if (GL::fboEnabled)
{
    /* Bind the last read buffer */
    GL::bindFramebuffer (GL_READ_FRAMEBUFFER, readBufferBinding);
}

You're right. Since this only happens in screenshot mode, I don't
think performance is a huge concern anways. glReadPixels is a far
slower operation than glBindFramebufferEXT anyways and we have to do
that.

On Fri, Apr 26, 2013 at 6:14 PM, MC Return <email address hidden> wrote:
> Review: Needs Information
>
> Sam, just 2 questions regarding this matter:
>
> 1. Are you aware that glReadBuffer (GL_FRONT); will just be called once per screenshot and just in the case FBOs are enabled, that means if the user is veryveryvery fast, maximally 3 times per second ?
>
> 2. Would it help to call glReadBuffer (GL_BACK); after glReadPixels (...) if (GL::fboEnabled) to change it back, or is
> this a stupid and useless idea ?
> --
> https://code.launchpad.net/~mc-return/compiz/compiz.merge-fix771875-screenshot-overlay-on-screenshots-with-enabled-fbos/+merge/160751
> Your team Compiz Maintainers is requested to review the proposed merge of lp:~mc-return/compiz/compiz.merge-fix771875-screenshot-overlay-on-screenshots-with-enabled-fbos into lp:compiz.

--
Sam Spilsbury

Revision history for this message
MC Return (mc-return) wrote :

> Actually, I think you're probably right on this one. Though I think if
> I remember correctly now the right approach is actually to read off of
> the currently bound framebuffer object and not the frontbuffer. The
> reason why the overlay persists is because the backbuffer is always
> one-frame-behind. I'm not really sure why there is no overlay on the
> frontbuffer, perhaps it is a race condition. In any case, those pixels
> will be at least one frame out of date.
>
> The way screenshot works is that it paints the overlay, and then upon
> "deciding" to take the screenshot, damages that region and then calls
> glReadPixels on the next paint pass. The pixels for that frame will be
> painted into the currently bound framebuffer object.
>
> So the correct approach would be:
>
> GLuint readBufferBinding = 0;
>
> if (GL::fboEnabled)
> {
> glGetIntegerv (GL_READ_FRAMEBUFFER, &readBufferBinding);
> /* We want to read off of the last frame, so bind the backbuffer */
> GL::bindFramebuffer (GL_READ_FRAMEBUFFER, 0);
> }
>
> ...
>
> glReadPixels
>
> ...
>
> if (GL::fboEnabled)
> {
> /* Bind the last read buffer */
> GL::bindFramebuffer (GL_READ_FRAMEBUFFER, readBufferBinding);
> }
>
> You're right. Since this only happens in screenshot mode, I don't
> think performance is a huge concern anways. glReadPixels is a far
> slower operation than glBindFramebufferEXT anyways and we have to do
> that.
>
Cool, I'll fix it that way then :)

Revision history for this message
MC Return (mc-return) wrote :

>
> So the correct approach would be:
>
> GLuint readBufferBinding = 0;
>
> if (GL::fboEnabled)
> {
> glGetIntegerv (GL_READ_FRAMEBUFFER, &readBufferBinding);
> /* We want to read off of the last frame, so bind the backbuffer */
> GL::bindFramebuffer (GL_READ_FRAMEBUFFER, 0);
> }
>
> ...
>
> glReadPixels
>
> ...
>
> if (GL::fboEnabled)
> {
> /* Bind the last read buffer */
> GL::bindFramebuffer (GL_READ_FRAMEBUFFER, readBufferBinding);
> }
>
This does not work:

/home/mcr2010/src/bzr/compiz/plugins/screenshot/src/screenshot.cpp: In member function ‘virtual void ShotScreen::paint(CompOutput::ptrList&, unsigned int)’:
/home/mcr2010/src/bzr/compiz/plugins/screenshot/src/screenshot.cpp:185:41: error: ‘readBufferBinding’ was not declared in this scope
    glGetIntegerv (GL_READ_FRAMEBUFFER, &readBufferBinding);
                                         ^
/home/mcr2010/src/bzr/compiz/plugins/screenshot/src/screenshot.cpp:196:46: error: ‘readBufferBinding’ was not declared in this scope
    GL::bindFramebuffer (GL_READ_FRAMEBUFFER, readBufferBinding);
                                              ^

Revision history for this message
MC Return (mc-return) :
review: Needs Information
Revision history for this message
Sam Spilsbury (smspillaz) wrote :

Did you define the readBufferBinding variable? Compiler says that you didn't ;-)

On Fri, Apr 26, 2013 at 9:34 PM, MC Return <email address hidden> wrote:
>>
>> So the correct approach would be:
>>
>> GLuint readBufferBinding = 0;
>>
>> if (GL::fboEnabled)
>> {
>> glGetIntegerv (GL_READ_FRAMEBUFFER, &readBufferBinding);
>> /* We want to read off of the last frame, so bind the backbuffer */
>> GL::bindFramebuffer (GL_READ_FRAMEBUFFER, 0);
>> }
>>
>> ...
>>
>> glReadPixels
>>
>> ...
>>
>> if (GL::fboEnabled)
>> {
>> /* Bind the last read buffer */
>> GL::bindFramebuffer (GL_READ_FRAMEBUFFER, readBufferBinding);
>> }
>>
> This does not work:
>
> /home/mcr2010/src/bzr/compiz/plugins/screenshot/src/screenshot.cpp: In member function ‘virtual void ShotScreen::paint(CompOutput::ptrList&, unsigned int)’:
> /home/mcr2010/src/bzr/compiz/plugins/screenshot/src/screenshot.cpp:185:41: error: ‘readBufferBinding’ was not declared in this scope
> glGetIntegerv (GL_READ_FRAMEBUFFER, &readBufferBinding);
> ^
> /home/mcr2010/src/bzr/compiz/plugins/screenshot/src/screenshot.cpp:196:46: error: ‘readBufferBinding’ was not declared in this scope
> GL::bindFramebuffer (GL_READ_FRAMEBUFFER, readBufferBinding);
> ^
>
>
> --
> https://code.launchpad.net/~mc-return/compiz/compiz.merge-fix771875-screenshot-overlay-on-screenshots-with-enabled-fbos/+merge/160751
> Your team Compiz Maintainers is requested to review the proposed merge of lp:~mc-return/compiz/compiz.merge-fix771875-screenshot-overlay-on-screenshots-with-enabled-fbos into lp:compiz.

--
Sam Spilsbury

Revision history for this message
MC Return (mc-return) wrote :

urgh - sorry

Revision history for this message
MC Return (mc-return) wrote :

GLint readBufferBinding = 0;

instead of

GLuint readBufferBinding = 0;

now did compile -> testing now... :)

Revision history for this message
MC Return (mc-return) wrote :

The overlay is on the screenshot again :(

Revision history for this message
MC Return (mc-return) wrote :

I used this code:

      GLint readBufferBinding = 0;

      /* If we have fbos enabled we
       * need to clear the overlay
       * before reading the pixels */
      if (GL::fboEnabled)
      {
   glGetIntegerv (GL_READ_FRAMEBUFFER, &readBufferBinding);
   /* We want to read off of the last frame, so bind the backbuffer */
   GL::bindFramebuffer (GL_READ_FRAMEBUFFER, 0);
      }

      glReadPixels (x1, ::screen->height () - y2, w, h,
      GL_RGBA, GL_UNSIGNED_BYTE,
      (GLvoid *) buffer);

      if (GL::fboEnabled)
   /* Bind the last read buffer */
   GL::bindFramebuffer (GL_READ_FRAMEBUFFER, readBufferBinding);

Revision history for this message
MC Return (mc-return) wrote :

Okay, updated the commit message here.
The last thing missing here seems to be your +1, Sam ;)

Revision history for this message
Sam Spilsbury (smspillaz) :
review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'plugins/opengl/include/opengl/opengl.h'
--- plugins/opengl/include/opengl/opengl.h 2013-01-10 09:23:24 +0000
+++ plugins/opengl/include/opengl/opengl.h 2013-04-27 11:54:25 +0000
@@ -449,6 +449,8 @@
449 /* OpenGL|ES does not support different draw/read framebuffers */449 /* OpenGL|ES does not support different draw/read framebuffers */
450 static const GLenum DRAW_FRAMEBUFFER = GL_FRAMEBUFFER;450 static const GLenum DRAW_FRAMEBUFFER = GL_FRAMEBUFFER;
451 static const GLenum READ_FRAMEBUFFER = GL_FRAMEBUFFER;451 static const GLenum READ_FRAMEBUFFER = GL_FRAMEBUFFER;
452 static const GLenum DRAW_FRAMEBUFFER_BINDING = FRAMEBUFFER_BINDING;
453 static const GLenum READ_FRAMEBUFFER_BINDING = FRAMEBUFFER_BINDING;
452454
453 static const GLenum FRAMEBUFFER_COMPLETE = GL_FRAMEBUFFER_COMPLETE;455 static const GLenum FRAMEBUFFER_COMPLETE = GL_FRAMEBUFFER_COMPLETE;
454 static const GLenum FRAMEBUFFER_UNDEFINED = 0;456 static const GLenum FRAMEBUFFER_UNDEFINED = 0;
@@ -484,6 +486,8 @@
484486
485 static const GLenum DRAW_FRAMEBUFFER = GL_DRAW_FRAMEBUFFER_EXT;487 static const GLenum DRAW_FRAMEBUFFER = GL_DRAW_FRAMEBUFFER_EXT;
486 static const GLenum READ_FRAMEBUFFER = GL_READ_FRAMEBUFFER_EXT;488 static const GLenum READ_FRAMEBUFFER = GL_READ_FRAMEBUFFER_EXT;
489 static const GLenum DRAW_FRAMEBUFFER_BINDING = GL_DRAW_FRAMEBUFFER_BINDING_EXT;
490 static const GLenum READ_FRAMEBUFFER_BINDING = GL_READ_FRAMEBUFFER_BINDING_EXT;
487 static const GLenum FRAMEBUFFER_COMPLETE = GL_FRAMEBUFFER_COMPLETE_EXT;491 static const GLenum FRAMEBUFFER_COMPLETE = GL_FRAMEBUFFER_COMPLETE_EXT;
488 static const GLenum FRAMEBUFFER_UNDEFINED = GL_FRAMEBUFFER_UNDEFINED;492 static const GLenum FRAMEBUFFER_UNDEFINED = GL_FRAMEBUFFER_UNDEFINED;
489 static const GLenum FRAMEBUFFER_INCOMPLETE_ATTACHMENT = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT;493 static const GLenum FRAMEBUFFER_INCOMPLETE_ATTACHMENT = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT;
490494
=== modified file 'plugins/screenshot/src/screenshot.cpp'
--- plugins/screenshot/src/screenshot.cpp 2013-04-24 15:42:11 +0000
+++ plugins/screenshot/src/screenshot.cpp 2013-04-27 11:54:25 +0000
@@ -23,6 +23,9 @@
23 * Author: David Reveman <davidr@novell.com>23 * Author: David Reveman <davidr@novell.com>
24 */24 */
2525
26#include <sstream>
27#include <boost/scoped_array.hpp>
28
26#include "screenshot.h"29#include "screenshot.h"
2730
28#include <dirent.h>31#include <dirent.h>
@@ -104,8 +107,6 @@
104 action->setState (action->state () & ~(CompAction::StateTermKey |107 action->setState (action->state () & ~(CompAction::StateTermKey |
105 CompAction::StateTermButton));108 CompAction::StateTermButton));
106109
107 gScreen->glPaintOutputSetEnabled (this, false);
108
109 return false;110 return false;
110}111}
111112
@@ -148,79 +149,298 @@
148ShotScreen::paint (CompOutput::ptrList &outputs,149ShotScreen::paint (CompOutput::ptrList &outputs,
149 unsigned int mask)150 unsigned int mask)
150{151{
151 cScreen->paint (outputs, mask);
152
153 if (mGrab)152 if (mGrab)
154 {153 {
155 if (!mGrabIndex)154 if (!mGrabIndex)
156 {155 {
157 int x1 = MIN (mX1, mX2);156 /* Taking screenshot, enable full paint on
158 int y1 = MIN (mY1, mY2);157 * this frame */
159 int x2 = MAX (mX1, mX2);158
160 int y2 = MAX (mY1, mY2);159 outputs.clear ();
161160 outputs.push_back (&screen->fullscreenOutput ());
162 int w = x2 - x1;161 }
163 int h = y2 - y1;162 }
164163
165 if (w && h)164 cScreen->paint (outputs, mask);
165}
166
167namespace
168{
169 bool paintSelectionRectangleFill (const CompRect &rect,
170 unsigned short *fillColor,
171 GLVertexBuffer *streamingBuffer,
172 const GLMatrix &transform)
173 {
174 GLfloat vertexData[12];
175 GLushort colorData[4];
176
177 int x1 = rect.x1 ();
178 int y1 = rect.y1 ();
179 int x2 = rect.x2 ();
180 int y2 = rect.y2 ();
181
182 const float MaxUShortFloat = std::numeric_limits <unsigned short>::max ();
183
184 /* draw filled rectangle */
185 float alpha = fillColor[3] / MaxUShortFloat;
186
187 colorData[0] = alpha * fillColor[0];
188 colorData[1] = alpha * fillColor[1];
189 colorData[2] = alpha * fillColor[2];
190 colorData[3] = alpha * MaxUShortFloat;
191
192 vertexData[0] = x1;
193 vertexData[1] = y1;
194 vertexData[2] = 0.0f;
195 vertexData[3] = x1;
196 vertexData[4] = y2;
197 vertexData[5] = 0.0f;
198 vertexData[6] = x2;
199 vertexData[7] = y1;
200 vertexData[8] = 0.0f;
201 vertexData[9] = x2;
202 vertexData[10] = y2;
203 vertexData[11] = 0.0f;
204
205 streamingBuffer->begin (GL_TRIANGLE_STRIP);
206
207 streamingBuffer->addColors (1, colorData);
208 streamingBuffer->addVertices (4, vertexData);
209
210 if (streamingBuffer->end ())
211 {
212 glEnable (GL_BLEND);
213
214 streamingBuffer->render (transform);
215
216 glDisable (GL_BLEND);
217
218 return true;
219 }
220
221 return false;
222 }
223
224 bool paintSelectionRectangleOutline (const CompRect &rect,
225 unsigned short *outlineColor,
226 GLVertexBuffer *streamingBuffer,
227 const GLMatrix &transform)
228 {
229 GLfloat vertexData[12];
230 GLushort colorData[4];
231
232 int x1 = rect.x1 ();
233 int y1 = rect.y1 ();
234 int x2 = rect.x2 ();
235 int y2 = rect.y2 ();
236
237 const float MaxUShortFloat = std::numeric_limits <unsigned short>::max ();
238
239 /* draw outline */
240 float alpha = outlineColor[3] / MaxUShortFloat;
241
242 colorData[0] = alpha * outlineColor[0];
243 colorData[1] = alpha * outlineColor[1];
244 colorData[2] = alpha * outlineColor[2];
245 colorData[3] = alpha * MaxUShortFloat;
246
247 vertexData[0] = x1;
248 vertexData[1] = y1;
249 vertexData[2] = 0.0f;
250 vertexData[3] = x1;
251 vertexData[4] = y2;
252 vertexData[5] = 0.0f;
253 vertexData[6] = x2;
254 vertexData[7] = y2;
255 vertexData[8] = 0.0f;
256 vertexData[9] = x2;
257 vertexData[10] = y1;
258 vertexData[11] = 0.0f;
259
260 streamingBuffer->begin (GL_LINE_LOOP);
261
262 streamingBuffer->addColors (1, colorData);
263 streamingBuffer->addVertices (4, vertexData);
264
265 if (streamingBuffer->end ())
266 {
267 glEnable (GL_BLEND);
268 glLineWidth (2.0);
269
270 streamingBuffer->render (transform);
271
272 glDisable (GL_BLEND);
273
274 return true;
275 }
276
277 return false;
278 }
279
280 void
281 ensureDirectoryForImage (CompString &directory)
282 {
283 /* If dir is empty, use user's desktop directory instead */
284 if (directory.length () == 0)
285 directory = getXDGUserDir (XDGUserDirDesktop);
286 }
287
288 int
289 getImageNumberFromDirectory (const CompString &directory)
290 {
291 struct dirent **namelist;
292
293 int n = scandir (directory.c_str (), &namelist, shotFilter, shotSort);
294
295 if (n >= 0)
296 {
297 int number = 0;
298
299 if (n > 0)
300 sscanf (namelist[n - 1]->d_name,
301 "screenshot%d.png",
302 &number);
303
304 ++number;
305
306 if (n)
307 free (namelist);
308
309 return number;
310 }
311 else
312 {
313 perror ("scandir");
314 return 0;
315 }
316 }
317
318 CompString
319 getImageAbsolutePath (const CompString &directory,
320 int number)
321 {
322 std::stringstream ss;
323 ss << directory << "/screenshot" << number << ".png";
324 return ss.str ();
325 }
326
327 bool
328 saveBuffer (const boost::scoped_array <GLubyte> &buffer,
329 int w,
330 int h,
331 const CompString &path)
332 {
333 CompSize imageSize (w, h);
334
335 if (!::screen->writeImageToFile (const_cast <CompString &> (path),
336 "png",
337 imageSize,
338 buffer.get ()))
339 {
340 compLogMessage ("screenshot", CompLogLevelError,
341 "failed to write screenshot image");
342 return false;
343 }
344
345 return true;
346 }
347
348 bool
349 launchApplicationAndTakeScreenshot (const CompString &app,
350 const CompString &directory)
351 {
352 if (app.length () > 0)
353 {
354 ::screen->runCommand (app + " " + directory);
355 return true;
356 }
357
358 return false;
359 }
360
361 bool
362 readFromGPUBufferToCPUBuffer (const CompRect &rect,
363 boost::scoped_array <GLubyte> &buffer)
364 {
365 int x1 = rect.x1 ();
366 int y1 = rect.y1 ();
367 int x2 = rect.x2 ();
368 int y2 = rect.y2 ();
369
370 int w = x2 - x1;
371 int h = y2 - y1;
372
373 if (w && h)
374 {
375 size_t size = w * h * 4;
376 buffer.reset (new GLubyte[size]);
377
378 if (buffer.get ())
166 {379 {
167 GLubyte *buffer;380 GLint drawBinding = 0;
168 CompString dir (optionGetDirectory ());381 GLint readBinding = 0;
169382
170 /* If dir is empty, use user's desktop directory instead */383 /* Bind the currently bound draw framebuffer to
171 if (dir.length () == 0)384 * the read framebuffer and read from it */
172 dir = getXDGUserDir (XDGUserDirDesktop);385 if (GL::fboEnabled)
173
174 buffer = (GLubyte *)malloc (sizeof (GLubyte) * w * h * 4);
175
176 if (buffer)
177 {386 {
178 struct dirent **namelist;387 glGetIntegerv (GL::DRAW_FRAMEBUFFER_BINDING, &drawBinding);
179388 glGetIntegerv (GL::READ_FRAMEBUFFER_BINDING, &readBinding);
180 glReadPixels (x1, ::screen->height () - y2, w, h,389 (GL::bindFramebuffer) (GL::READ_FRAMEBUFFER, drawBinding);
181 GL_RGBA, GL_UNSIGNED_BYTE,
182 (GLvoid *) buffer);
183
184 int n = scandir (dir.c_str (), &namelist, shotFilter, shotSort);
185
186 if (n >= 0)
187 {
188 char name[256];
189 int number = 0;
190
191 if (n > 0)
192 sscanf (namelist[n - 1]->d_name,
193 "screenshot%d.png",
194 &number);
195
196 ++number;
197
198 if (n)
199 free (namelist);
200
201 snprintf (name, 256, "screenshot%d.png", number);
202
203 CompString app (optionGetLaunchApp ());
204 CompString path (dir + "/" + name);
205 CompSize imageSize (w, h);
206
207 if (!::screen->writeImageToFile (path, "png",
208 imageSize, buffer))
209 compLogMessage ("screenshot", CompLogLevelError,
210 "failed to write screenshot image");
211 else if (app.length () > 0)
212 ::screen->runCommand (app + " " + path);
213 }
214 else
215 perror (dir.c_str ());
216
217 free (buffer);
218 }390 }
391
392 glGetError ();
393 glReadPixels (x1, ::screen->height () - y2, w, h,
394 GL_RGBA, GL_UNSIGNED_BYTE,
395 (GLvoid *) buffer.get ());
396
397 if (GL::fboEnabled)
398 (GL::bindFramebuffer) (GL::READ_FRAMEBUFFER, readBinding);
399
400 if (glGetError () != GL_NO_ERROR)
401 return false;
402
403 return true;
219 }404 }
220 /* Disable screen capture */405 }
221 cScreen->paintSetEnabled (this, false);406
222 mGrab = false;407 return false;
223 }408 }
409
410 /* We need to take directory by copy because
411 * it may be modified later */
412 bool saveScreenshot (CompRect rect,
413 CompString directory,
414 const CompString &alternativeApplication)
415 {
416 ensureDirectoryForImage (directory);
417
418 int number = getImageNumberFromDirectory (directory);
419 CompString path = getImageAbsolutePath (directory, number);
420
421 boost::scoped_array <GLubyte> buffer;
422
423 bool success = readFromGPUBufferToCPUBuffer (rect,
424 buffer);
425
426 if (success)
427 {
428 success = saveBuffer (buffer,
429 rect.width (),
430 rect.height (), path);
431 }
432 else
433 {
434 compLogMessage ("screenshot", CompLogLevelWarn, "glReadPixels failed");
435 }
436
437 if (!success)
438 success =
439 launchApplicationAndTakeScreenshot (alternativeApplication,
440 directory);
441
442 return success;
443
224 }444 }
225}445}
226446
@@ -231,12 +451,6 @@
231 CompOutput *output,451 CompOutput *output,
232 unsigned int mask)452 unsigned int mask)
233{453{
234 GLVertexBuffer *streamingBuffer = GLVertexBuffer::streamingBuffer ();
235 GLMatrix transform (matrix);
236 GLfloat vertexData[12];
237 GLushort colorData[4];
238 GLushort *color;
239
240 bool status = gScreen->glPaintOutput (attrib, matrix, region, output, mask);454 bool status = gScreen->glPaintOutput (attrib, matrix, region, output, mask);
241455
242 if (status && mGrab)456 if (status && mGrab)
@@ -245,87 +459,42 @@
245 * we are grabbed, the size has changed and the CCSM459 * we are grabbed, the size has changed and the CCSM
246 * option to draw it is enabled. */460 * option to draw it is enabled. */
247461
462 CompRect selectionRect (std::min (mX1, mX2),
463 std::min (mY1, mY2),
464 std::abs (mX2 - mX1),
465 std::abs (mY2 - mY1));
466
248 if (mGrabIndex &&467 if (mGrabIndex &&
249 selectionSizeChanged &&468 selectionSizeChanged &&
250 optionGetDrawSelectionIndicator ())469 optionGetDrawSelectionIndicator ())
251 {470 {
252 int x1 = MIN (mX1, mX2);471 GLMatrix transform (matrix);
253 int y1 = MIN (mY1, mY2);472 GLVertexBuffer *streamingBuffer (GLVertexBuffer::streamingBuffer ());
254 int x2 = MAX (mX1, mX2);473 transform.toScreenSpace (output, -DEFAULT_Z_CAMERA);
255 int y2 = MAX (mY1, mY2);474
256475 paintSelectionRectangleFill (selectionRect,
257 const float MaxUShortFloat = std::numeric_limits <unsigned short>::max ();476 optionGetSelectionFillColor (),
258477 streamingBuffer,
259 /* draw filled rectangle */478 transform);
260 float alpha = optionGetSelectionFillColorAlpha () / MaxUShortFloat;479
261 color = optionGetSelectionFillColor ();480 paintSelectionRectangleOutline (selectionRect,
262481 optionGetSelectionOutlineColor (),
263 colorData[0] = alpha * color[0];482 streamingBuffer,
264 colorData[1] = alpha * color[1];483 transform);
265 colorData[2] = alpha * color[2];
266 colorData[3] = alpha * MaxUShortFloat;
267
268 vertexData[0] = x1;
269 vertexData[1] = y1;
270 vertexData[2] = 0.0f;
271 vertexData[3] = x1;
272 vertexData[4] = y2;
273 vertexData[5] = 0.0f;
274 vertexData[6] = x2;
275 vertexData[7] = y1;
276 vertexData[8] = 0.0f;
277 vertexData[9] = x2;
278 vertexData[10] = y2;
279 vertexData[11] = 0.0f;
280
281 transform.translate (-0.5f, -0.5f, -DEFAULT_Z_CAMERA);
282 transform.scale (1.0f / output->width (),
283 -1.0f / output->height (),
284 1.0f);
285 transform.translate (-output->region ()->extents.x1,
286 -output->region ()->extents.y2,
287 0.0f);
288
289 glEnable (GL_BLEND);
290
291 streamingBuffer->begin (GL_TRIANGLE_STRIP);
292
293 streamingBuffer->addColors (1, colorData);
294 streamingBuffer->addVertices (4, vertexData);
295
296 streamingBuffer->end ();
297 streamingBuffer->render (transform);
298
299 /* draw outline */
300 alpha = optionGetSelectionOutlineColorAlpha () / MaxUShortFloat;
301 color = optionGetSelectionOutlineColor ();
302
303 colorData[0] = alpha * color[0];
304 colorData[1] = alpha * color[1];
305 colorData[2] = alpha * color[2];
306 colorData[3] = alpha * MaxUShortFloat;
307
308 vertexData[6] = x2;
309 vertexData[7] = y2;
310 vertexData[9] = x2;
311 vertexData[10] = y1;
312
313 glLineWidth (2.0);
314
315 streamingBuffer->begin (GL_LINE_LOOP);
316
317 streamingBuffer->addColors (1, colorData);
318 streamingBuffer->addVertices (4, vertexData);
319
320 streamingBuffer->end ();
321 streamingBuffer->render (transform);
322
323 glDisable (GL_BLEND);
324484
325 /* we finished painting the selection box,485 /* we finished painting the selection box,
326 * reset selectionSizeChanged now */486 * reset selectionSizeChanged now */
327 selectionSizeChanged = false;487 selectionSizeChanged = false;
328 }488 }
489 else if (!mGrabIndex)
490 {
491 /* Taking a screenshot */
492 saveScreenshot (selectionRect,
493 optionGetDirectory (),
494 optionGetLaunchApp ());
495 cScreen->paintSetEnabled (this, false);
496 gScreen->glPaintOutputSetEnabled (this, false);
497 }
329 }498 }
330499
331 return status;500 return status;

Subscribers

People subscribed via source and target branches

to all changes: