1
/* * This file is part of Maliit framework *
3
* Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
6
* Contact: maliit-discuss@lists.maliit.org
8
* This library is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU Lesser General Public
10
* License version 2.1 as published by the Free Software Foundation
11
* and appearing in the file LICENSE.LGPL included in the packaging
15
#include "mimrotationanimation.h"
17
#include "mimremotewindow.h"
18
#include "mimxapplication.h"
19
#include "mpassthruwindow.h"
20
#include "mimserveroptions.h"
23
#include <QDesktopWidget>
24
#include <QGraphicsScene>
26
#include <QPropertyAnimation>
27
#include <QResizeEvent>
31
#include <X11/Xatom.h>
32
#include <X11/Xutil.h>
34
SnapshotPixmapItem::SnapshotPixmapItem(QGraphicsItem *parent) :
36
QGraphicsPixmapItem(parent)
40
SnapshotPixmapItem::SnapshotPixmapItem(QPixmap pixmap, QGraphicsItem *parent) :
42
QGraphicsPixmapItem(pixmap, parent)
46
SnapshotPixmapItem::~SnapshotPixmapItem() {}
49
MImDamageMonitor::MImDamageMonitor(MImRemoteWindow* remoteWin,
55
timeoutTimer.setInterval(1000);
56
timeoutTimer.setSingleShot(true);
57
connect(&timeoutTimer,SIGNAL(timeout()),
58
this,SLOT(timeoutExpired()));
60
remoteWindowChanged(remoteWin);
64
MImDamageMonitor::remoteWindowChanged(MImRemoteWindow *newRemoteWindow)
66
remoteWindow = newRemoteWindow;
70
MImDamageMonitor::activate()
72
damageDetected = false;
75
connect(remoteWindow, SIGNAL(contentUpdated(QRegion)),
76
this, SLOT(contentUpdated(QRegion)));
80
MImDamageMonitor::waitForDamage()
84
qDebug() << __PRETTY_FUNCTION__ << " - damage already received, emitting signal.";
86
Q_EMIT damageReceivedOrTimeout();
91
MImDamageMonitor::contentUpdated(QRegion)
93
damageDetected = true;
94
// Emit signal only after client called waitForDamage().
95
if (timeoutTimer.isActive()) {
96
qDebug() << __PRETTY_FUNCTION__ << " - damage received, emitting signal.";
98
Q_EMIT damageReceivedOrTimeout();
103
MImDamageMonitor::cancel()
107
disconnect(remoteWindow, SIGNAL(contentUpdated(QRegion)),
108
this, SLOT(contentUpdated(QRegion)));
113
MImDamageMonitor::timeoutExpired()
115
qDebug() << __PRETTY_FUNCTION__;
117
Q_EMIT damageReceivedOrTimeout();
121
qreal rotateAngle(int startAngle, int endAngle) {
122
// Special cases ensuring shortest rotation angle according to original code from
123
// MCrossFadedOrientationAnimationPrivate::setRootElementRotationAnimationValues.
124
if (startAngle == 270 && endAngle == 90) {
126
} else if (startAngle == 180 && endAngle == 0) {
128
} else if (startAngle == 270 && endAngle == 0) {
130
} else if (startAngle == 0 && endAngle == 270) {
133
return endAngle - startAngle;
138
MImRotationAnimation::MImRotationAnimation(QWidget* snapshotWidget,
139
QWidget* passThroughWindow,
140
MImXServerLogic *serverLogic,
141
const MImServerXOptions &options) :
142
QGraphicsView(new QGraphicsScene(), passThroughWindow),
144
snapshotWidget(snapshotWidget),
146
animationStartPixmapItem(0),
147
animationEndPixmapItem(0),
148
startOrientationAngle(0),
149
currentOrientationAngle(0),
150
aboutToChangeReceived(false),
153
mServerLogic(serverLogic)
155
// Animation plays on top of a black backround,
156
// covering up the underlying application.
157
setBackgroundBrush(QBrush(Qt::black));
159
// Get rid of Meegotouch decorations.
160
setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
162
// Avoid drawing transparent pixel borders.
163
setFrameShape(QFrame::NoFrame);
165
// We do not want input focus for that window.
166
setAttribute(Qt::WA_X11DoNotAcceptFocus);
168
// Avoid a white flicker when popping up our
169
// extra window for the animation.
170
setAttribute(Qt::WA_NoSystemBackground);
171
setAttribute(Qt::WA_OpaquePaintEvent);
172
viewport()->setAttribute(Qt::WA_NoSystemBackground);
173
viewport()->setAttribute(Qt::WA_OpaquePaintEvent);
175
// Remove all animation elements from scene and hide.
176
connect(&rotationAnimationGroup, SIGNAL(finished()),
177
this, SLOT(clearScene()));
179
connect(mServerLogic, SIGNAL(remoteWindowChanged(MImRemoteWindow*)),
180
this, SLOT(remoteWindowChanged(MImRemoteWindow*)), Qt::UniqueConnection);
182
damageMonitor = new MImDamageMonitor(remoteWindow, this);
183
connect(damageMonitor, SIGNAL(damageReceivedOrTimeout()),
184
this, SLOT(startAnimation()));
188
if (QApplication::desktop()) {
189
resize(QApplication::desktop()->screenGeometry().size());
194
MImRotationAnimation::cancelAnimation()
196
// disableAnimation() slot is connected to passThruWindowUnmapped()
197
// So, in order to be on the safe side. We clean the scene.
198
if (rotationAnimationGroup.state() != QAbstractAnimation::Stopped) {
199
rotationAnimationGroup.stop();
202
damageMonitor->cancel();
205
aboutToChangeReceived = false;
210
MImRotationAnimation::resizeEvent(QResizeEvent *event)
212
setSceneRect(QRect(QPoint(0,0),event->size()));
216
MImRotationAnimation::setupScene()
218
animationStartPixmapItem = new SnapshotPixmapItem(compositeWindowStart);
219
// See crossfadedorientationanimationstyle.css for the origin of these values.
220
animationStartPixmapItem->setTransformOriginPoint(240,240);
221
animationStartPixmapItem->setPos(0,0);
223
scene()->addItem(animationStartPixmapItem);
228
MImRotationAnimation::showInitial()
231
// We need to be on the screen really fast, otherwise you would
232
// see the meegotouch orientation change in the window under us.
233
// Going through the event loop will take about 800ms, which is too long.
234
// Thus, requesting a synchronous update here.
235
// TODO: Not a perfect solution, in rare cases and high load situations
236
// application is updating the screen faster than meego-im-uiserver.
241
MImRotationAnimation::showEvent(QShowEvent*) {
242
// Setting _NET_WM_WINDOW_TYPE to _NET_WM_WINDOW_TYPE_INPUT
243
// prevents the underlying app from losing focus and
244
// avoid window decorations such as the slide-in animation.
245
static Atom input = XInternAtom(QX11Info::display(), "_NET_WM_WINDOW_TYPE_INPUT", False);
246
static Atom window_type = XInternAtom(QX11Info::display(), "_NET_WM_WINDOW_TYPE", False);
247
XChangeProperty(QX11Info::display(), effectiveWinId(), window_type, XA_ATOM, 32,
248
PropModeReplace, (unsigned char *) &input, 1);
252
MImRotationAnimation::setupAnimation(int fromAngle, int toAngle) {
254
qreal hiddenOpacity = 0;
255
qreal visibleOpacity = 1.0;
257
// See crossfadedorientationanimationstyle.css for the origin of the animation parameters.
260
QPropertyAnimation* startAnimations[2];
261
QPropertyAnimation* endAnimations[4];
263
startAnimations[0] = new QPropertyAnimation();
264
startAnimations[1] = new QPropertyAnimation();
265
endAnimations[0] = new QPropertyAnimation();
266
endAnimations[1] = new QPropertyAnimation();
267
endAnimations[2] = new QPropertyAnimation();
268
endAnimations[3] = new QPropertyAnimation();
270
int initialRotationOfTarget = fromAngle - toAngle;
271
int rotateBy = rotateAngle(fromAngle, toAngle);
273
QPixmap remoteWindowPixmap;
275
// This is the live pixmap of the application coming from X.
276
// It stays live if it's used in read-only mode.
277
remoteWindowPixmap = remoteWindow->windowPixmap();
279
animationEndPixmapItem = new SnapshotPixmapItem(remoteWindowPixmap);
280
animationEndPixmapItem->setPos(0,0);
281
animationEndPixmapItem->setTransformOriginPoint(240,240);
282
animationEndPixmapItem->setRotation(initialRotationOfTarget);
283
animationEndPixmapItem->setOpacity(hiddenOpacity);
285
animationEndVkbOverlayItem = new SnapshotPixmapItem(grabVkbOnly());
286
animationEndVkbOverlayItem->setPos(0,0);
287
animationEndVkbOverlayItem->setTransformOriginPoint(240,240);
288
animationEndVkbOverlayItem->setRotation(initialRotationOfTarget);
289
animationEndVkbOverlayItem->setOpacity(hiddenOpacity);
292
scene()->addItem(animationEndPixmapItem);
293
scene()->addItem(animationEndVkbOverlayItem);
296
startAnimations[0]->setPropertyName("rotation");
297
startAnimations[0]->setStartValue(0);
298
startAnimations[0]->setEndValue(rotateBy);
299
startAnimations[0]->setEasingCurve(QEasingCurve::InOutExpo);
300
startAnimations[0]->setDuration(duration);
302
startAnimations[1]->setPropertyName("opacity");
303
startAnimations[1]->setStartValue(visibleOpacity);
304
startAnimations[1]->setEndValue(hiddenOpacity);
305
startAnimations[1]->setEasingCurve(QEasingCurve::InOutExpo);
306
startAnimations[1]->setDuration(duration);
308
endAnimations[0]->setPropertyName("rotation");
309
endAnimations[0]->setStartValue(initialRotationOfTarget);
310
endAnimations[0]->setEndValue(initialRotationOfTarget+rotateBy);
311
endAnimations[0]->setEasingCurve(QEasingCurve::InOutExpo);
312
endAnimations[0]->setDuration(duration);
314
endAnimations[1]->setPropertyName("opacity");
315
endAnimations[1]->setStartValue(hiddenOpacity);
316
endAnimations[1]->setEndValue(visibleOpacity);
317
endAnimations[1]->setEasingCurve(QEasingCurve::InOutExpo);
318
endAnimations[1]->setDuration(duration);
320
// same for vkb overlay
321
endAnimations[2]->setPropertyName("rotation");
322
endAnimations[2]->setStartValue(initialRotationOfTarget);
323
endAnimations[2]->setEndValue(initialRotationOfTarget+rotateBy);
324
endAnimations[2]->setEasingCurve(QEasingCurve::InOutExpo);
325
endAnimations[2]->setDuration(duration);
327
endAnimations[3]->setPropertyName("opacity");
328
endAnimations[3]->setStartValue(hiddenOpacity);
329
endAnimations[3]->setEndValue(visibleOpacity);
330
endAnimations[3]->setEasingCurve(QEasingCurve::InOutExpo);
331
endAnimations[3]->setDuration(duration);
333
startAnimations[0]->setTargetObject(animationStartPixmapItem);
334
startAnimations[1]->setTargetObject(animationStartPixmapItem);
335
endAnimations[0]->setTargetObject(animationEndPixmapItem);
336
endAnimations[1]->setTargetObject(animationEndPixmapItem);
337
endAnimations[2]->setTargetObject(animationEndVkbOverlayItem);
338
endAnimations[3]->setTargetObject(animationEndVkbOverlayItem);
341
rotationAnimationGroup.clear();
342
rotationAnimationGroup.addAnimation(startAnimations[0]);
343
rotationAnimationGroup.addAnimation(startAnimations[1]);
344
rotationAnimationGroup.addAnimation(endAnimations[0]);
345
rotationAnimationGroup.addAnimation(endAnimations[1]);
346
rotationAnimationGroup.addAnimation(endAnimations[2]);
347
rotationAnimationGroup.addAnimation(endAnimations[3]);
350
MImRotationAnimation::~MImRotationAnimation() {
351
QGraphicsScene *myScene = scene();
355
delete damageMonitor;
358
rotationAnimationGroup.clear();
363
MImRotationAnimation::grabComposited()
365
if (!remoteWindow || remoteWindow->windowPixmap().isNull()) {
369
// Explicitly copying here since we want to paint the keyboard on top.
370
// Qt is unable to render text into QPixmap, therefore we use QImage here.
371
QImage grabImage(remoteWindow->windowPixmap().toImage());
373
// Overlay keyboard, transparency not required
374
QPainter painter(&grabImage);
375
snapshotWidget->render(&painter,QPoint(0,0),QRect(0,0,width(),height()));
377
return QPixmap::fromImage(grabImage);
381
MImRotationAnimation::grabVkbOnly()
383
mServerLogic->setSuppressBackground(true);
384
// We need to work with a QImage here, otherwise we lose the
385
// transparency of the see-through part of the keyboard image.
386
QImage grabImage(size(),QImage::Format_ARGB32);
387
grabImage.fill(Qt::transparent);
389
// Fill empty QImage with keyboard snapshot.
390
QPainter painter(&grabImage);
391
snapshotWidget->render(&painter,QPoint(0,0),QRect(0,0,width(),height()));
392
// QPainter needs to be closed before returning
393
// new QPixmap generated from QImage.
396
mServerLogic->setSuppressBackground(false);
398
return QPixmap::fromImage(grabImage);
402
MImRotationAnimation::remoteWindowChanged(MImRemoteWindow* newRemoteWindow) {
403
remoteWindow = newRemoteWindow;
404
damageMonitor->remoteWindowChanged(newRemoteWindow);
406
// Stop playing animations when underlying window is unmapped.
408
qDebug() << __PRETTY_FUNCTION__ << " - remote window gone, cancelling animation.";
414
MImRotationAnimation::appOrientationAboutToChange(int toAngle) {
415
qDebug() << __PRETTY_FUNCTION__ << " - toAngle: " << toAngle;
417
if (!mServerLogic->passThruWindow()->isVisible()
418
|| toAngle == currentOrientationAngle
419
|| aboutToChangeReceived) {
422
startOrientationAngle = currentOrientationAngle;
424
// Assuming that in self-composited case we don't need
425
// extra redirection, we're already redirected.
426
if (xOptions.selfComposited && remoteWindow) {
427
remoteWindow->redirect();
430
// Capturing initial snapshot that's used for the beginning of the rotation.
431
compositeWindowStart = grabComposited();
433
// Do not do anything if no initial snapshot is available
434
if (compositeWindowStart.isNull())
437
// Clean up if we were still in the middle of a previous rotation animation.
438
rotationAnimationGroup.stop();
446
damageMonitor->activate();
448
// Unfortunately, we need to shield against appOrientationChangeFinishedEvents
449
// that come in without a previous "AboutToChange" event.
450
aboutToChangeReceived = true;
454
MImRotationAnimation::appOrientationChangeFinished(int toAngle) {
455
qDebug() << __PRETTY_FUNCTION__ << " - toAngle: " << toAngle
456
<< " startOrientationAngle: " << startOrientationAngle;
458
currentOrientationAngle = toAngle;
460
if (!mServerLogic->passThruWindow()->isVisible()
461
|| toAngle == startOrientationAngle
462
|| !aboutToChangeReceived) {
464
aboutToChangeReceived = false;
468
setupAnimation(startOrientationAngle, toAngle);
470
damageMonitor->waitForDamage();
475
MImRotationAnimation::startAnimation()
477
qDebug() << __PRETTY_FUNCTION__;
479
rotationAnimationGroup.start();
480
aboutToChangeReceived = false;
483
void MImRotationAnimation::clearScene() {
484
// When self-compositing is off, we don't need to maintain
486
if (not xOptions.selfComposited && remoteWindow) {
487
remoteWindow->unredirect();
496
compositeWindowStart = QPixmap();