KWin
Loading...
Searching...
No Matches
wobblywindows.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2008 Cédric Borgese <cedric.borgese@gmail.com>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9
10#include "wobblywindows.h"
12#include "wobblywindowsconfig.h"
13
14#include <cmath>
15
16//#define COMPUTE_STATS
17
18// if you enable it and run kwin in a terminal from the session it manages,
19// be sure to redirect the output of kwin in a file or
20// you'll propably get deadlocks.
21//#define VERBOSE_MODE
22
23#if defined COMPUTE_STATS && !defined VERBOSE_MODE
24#ifdef __GNUC__
25#warning "You enable COMPUTE_STATS without VERBOSE_MODE, computed stats will not be printed."
26#endif
27#endif
28
29Q_LOGGING_CATEGORY(KWIN_WOBBLYWINDOWS, "kwin_effect_wobblywindows", QtWarningMsg)
30
31namespace KWin
32{
33
50
51static const ParameterSet set_0 = {
52 0.15,
53 0.80,
54 0.10,
55 20.0,
56 20.0,
57 0.0,
58 1000.0,
59 0.5,
60 0.0,
61 1000.0,
62 0.5,
63};
64
65static const ParameterSet set_1 = {
66 0.10,
67 0.85,
68 0.10,
69 20.0,
70 20.0,
71 0.0,
72 1000.0,
73 0.5,
74 0.0,
75 1000.0,
76 0.5,
77};
78
79static const ParameterSet set_2 = {
80 0.06,
81 0.90,
82 0.10,
83 20.0,
84 20.0,
85 0.0,
86 1000.0,
87 0.5,
88 0.0,
89 1000.0,
90 0.5,
91};
92
93static const ParameterSet set_3 = {
94 0.03,
95 0.92,
96 0.20,
97 20.0,
98 20.0,
99 0.0,
100 1000.0,
101 0.5,
102 0.0,
103 1000.0,
104 0.5,
105};
106
107static const ParameterSet set_4 = {
108 0.01,
109 0.97,
110 0.25,
111 20.0,
112 20.0,
113 0.0,
114 1000.0,
115 0.5,
116 0.0,
117 1000.0,
118 0.5,
119};
120
121static const ParameterSet pset[5] = {set_0, set_1, set_2, set_3, set_4};
122
124{
125 WobblyWindowsConfig::instance(effects->config());
128
129 const auto windows = effects->stackingOrder();
130 for (EffectWindow *window : windows) {
131 slotWindowAdded(window);
132 }
133}
134
136{
137 if (!windows.empty()) {
138 // we should be empty at this point...
139 qCDebug(KWIN_WOBBLYWINDOWS) << "Windows list not empty. Left items : " << windows.count();
140 }
141}
142
144{
145 WobblyWindowsConfig::self()->read();
146
147 QString settingsMode = WobblyWindowsConfig::settings();
148 if (settingsMode != QStringLiteral("Custom")) {
149 unsigned int wobblynessLevel = WobblyWindowsConfig::wobblynessLevel();
150 if (wobblynessLevel > 4) {
151 qCDebug(KWIN_WOBBLYWINDOWS) << "Wrong value for \"WobblynessLevel\" : " << wobblynessLevel;
152 wobblynessLevel = 4;
153 }
154 setParameterSet(pset[wobblynessLevel]);
155
156 if (WobblyWindowsConfig::advancedMode()) {
157 m_stiffness = WobblyWindowsConfig::stiffness() / 100.0;
158 m_drag = WobblyWindowsConfig::drag() / 100.0;
159 m_move_factor = WobblyWindowsConfig::moveFactor() / 100.0;
160 }
161 } else { // Custom method, read all values from config file.
162 m_stiffness = WobblyWindowsConfig::stiffness() / 100.0;
163 m_drag = WobblyWindowsConfig::drag() / 100.0;
164 m_move_factor = WobblyWindowsConfig::moveFactor() / 100.0;
165
166 m_xTesselation = WobblyWindowsConfig::xTesselation();
167 m_yTesselation = WobblyWindowsConfig::yTesselation();
168
169 m_minVelocity = WobblyWindowsConfig::minVelocity();
170 m_maxVelocity = WobblyWindowsConfig::maxVelocity();
171 m_stopVelocity = WobblyWindowsConfig::stopVelocity();
172 m_minAcceleration = WobblyWindowsConfig::minAcceleration();
173 m_maxAcceleration = WobblyWindowsConfig::maxAcceleration();
174 m_stopAcceleration = WobblyWindowsConfig::stopAcceleration();
175 }
176
177 m_moveWobble = WobblyWindowsConfig::moveWobble();
178 m_resizeWobble = WobblyWindowsConfig::resizeWobble();
179
180#if defined VERBOSE_MODE
181 qCDebug(KWIN_WOBBLYWINDOWS) << "Parameters :\n"
182 << "grid(" << m_stiffness << ", " << m_drag << ", " << m_move_factor << ")\n"
183 << "velocity(" << m_minVelocity << ", " << m_maxVelocity << ", " << m_stopVelocity << ")\n"
184 << "acceleration(" << m_minAcceleration << ", " << m_maxAcceleration << ", " << m_stopAcceleration << ")\n"
185 << "tesselation(" << m_xTesselation << ", " << m_yTesselation << ")";
186#endif
187}
188
193
194void WobblyWindowsEffect::setParameterSet(const ParameterSet &pset)
195{
196 m_stiffness = pset.stiffness;
197 m_drag = pset.drag;
198 m_move_factor = pset.move_factor;
199
200 m_xTesselation = pset.xTesselation;
201 m_yTesselation = pset.yTesselation;
202
203 m_minVelocity = pset.minVelocity;
204 m_maxVelocity = pset.maxVelocity;
205 m_stopVelocity = pset.stopVelocity;
206 m_minAcceleration = pset.minAcceleration;
207 m_maxAcceleration = pset.maxAcceleration;
208 m_stopAcceleration = pset.stopAcceleration;
209}
210
212{
213 this->m_minVelocity = m_minVelocity;
214}
215
217{
218 m_move_factor = factor;
219}
220
222{
223 m_stiffness = stiffness;
224}
225
227{
228 m_drag = drag;
229}
230
231void WobblyWindowsEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
232{
233 effects->prePaintScreen(data, presentTime);
234}
235
236static const std::chrono::milliseconds integrationStep(10);
237
238void WobblyWindowsEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime)
239{
240 auto infoIt = windows.find(w);
241 if (infoIt != windows.end()) {
242 data.setTransformed();
243
244 while ((presentTime - infoIt->clock).count() > 0) {
245 const auto delta = std::min(presentTime - infoIt->clock, integrationStep);
246 infoIt->clock += delta;
247
248 if (!updateWindowWobblyDatas(w, delta.count())) {
249 break;
250 }
251 }
252 }
253
254 effects->prePaintWindow(w, data, presentTime);
255}
256
258{
259 if (!(mask & PAINT_SCREEN_TRANSFORMED) && windows.contains(w)) {
260 WindowWobblyInfos &wwi = windows[w];
261 if (!wwi.wobblying) {
262 return;
263 }
264
265 int tx = w->frameGeometry().x();
266 int ty = w->frameGeometry().y();
267 int width = w->frameGeometry().width();
268 int height = w->frameGeometry().height();
269 double left = 0.0;
270 double top = 0.0;
271 double right = w->width();
272 double bottom = w->height();
273
274 quads = quads.makeRegularGrid(m_xTesselation, m_yTesselation);
275 for (int i = 0; i < quads.count(); ++i) {
276 for (int j = 0; j < 4; ++j) {
277 WindowVertex &v = quads[i][j];
278 Pair uv = {v.x() / width, v.y() / height};
279 Pair newPos = computeBezierPoint(wwi, uv);
280 v.move(newPos.x - tx, newPos.y - ty);
281 }
282 left = std::min(left, quads[i].left());
283 top = std::min(top, quads[i].top());
284 right = std::max(right, quads[i].right());
285 bottom = std::max(bottom, quads[i].bottom());
286 }
287 QRectF dirtyRect(
288 left * data.xScale() + w->x() + data.xTranslation(),
289 top * data.yScale() + w->y() + data.yTranslation(),
290 (right - left + 1.0) * data.xScale(),
291 (bottom - top + 1.0) * data.yScale());
292 // Expand the dirty region by 1px to fix potential round/floor issues.
293 dirtyRect.adjust(-1.0, -1.0, 1.0, 1.0);
294 m_updateRegion = m_updateRegion.united(dirtyRect.toRect());
295 }
296}
297
299{
300 if (!m_updateRegion.isEmpty()) {
301 effects->addRepaint(m_updateRegion);
302 m_updateRegion = QRegion();
303 }
304
305 // Call the next effect.
307}
308
316
318{
319 if (w->isSpecialWindow()) {
320 return;
321 }
322
323 if ((w->isUserMove() && m_moveWobble) || (w->isUserResize() && m_resizeWobble)) {
324 startMovedResized(w);
325 }
326}
327
329{
330 if (windows.contains(w)) {
331 WindowWobblyInfos &wwi = windows[w];
332 const QRectF rect = w->frameGeometry();
333 if (rect.y() != wwi.resize_original_rect.y()) {
334 wwi.can_wobble_top = true;
335 }
336 if (rect.x() != wwi.resize_original_rect.x()) {
337 wwi.can_wobble_left = true;
338 }
339 if (rect.right() != wwi.resize_original_rect.right()) {
340 wwi.can_wobble_right = true;
341 }
342 if (rect.bottom() != wwi.resize_original_rect.bottom()) {
343 wwi.can_wobble_bottom = true;
344 }
346 }
347}
348
350{
351 if (windows.contains(w)) {
352 WindowWobblyInfos &wwi = windows[w];
353 wwi.status = Free;
354 const QRectF rect = w->frameGeometry();
355 if (rect.y() != wwi.resize_original_rect.y()) {
356 wwi.can_wobble_top = true;
357 }
358 if (rect.x() != wwi.resize_original_rect.x()) {
359 wwi.can_wobble_left = true;
360 }
361 if (rect.right() != wwi.resize_original_rect.right()) {
362 wwi.can_wobble_right = true;
363 }
364 if (rect.bottom() != wwi.resize_original_rect.bottom()) {
365 wwi.can_wobble_bottom = true;
366 }
367 }
368}
369
371{
372 if (w->isUserMove() || w->isSpecialWindow()) {
373 return;
374 }
375
376 if (m_moveWobble && m_resizeWobble) {
377 stepMovedResized(w);
378 }
379
380 if (windows.contains(w)) {
381 WindowWobblyInfos &wwi = windows[w];
382 const QRectF rect = w->frameGeometry();
383 if (rect.y() != wwi.resize_original_rect.y()) {
384 wwi.can_wobble_top = true;
385 }
386 if (rect.x() != wwi.resize_original_rect.x()) {
387 wwi.can_wobble_left = true;
388 }
389 if (rect.right() != wwi.resize_original_rect.right()) {
390 wwi.can_wobble_right = true;
391 }
392 if (rect.bottom() != wwi.resize_original_rect.bottom()) {
393 wwi.can_wobble_bottom = true;
394 }
395 }
396}
397
398void WobblyWindowsEffect::startMovedResized(EffectWindow *w)
399{
400 if (!windows.contains(w)) {
401 WindowWobblyInfos new_wwi;
402 initWobblyInfo(new_wwi, w->frameGeometry());
403 windows[w] = new_wwi;
404 redirect(w);
405 }
406
407 WindowWobblyInfos &wwi = windows[w];
408 wwi.status = Moving;
409 const QRectF &rect = w->frameGeometry();
410
411 qreal x_increment = rect.width() / (wwi.width - 1.0);
412 qreal y_increment = rect.height() / (wwi.height - 1.0);
413
414 Pair picked = {static_cast<qreal>(cursorPos().x()), static_cast<qreal>(cursorPos().y())};
415 int indx = (picked.x - rect.x()) / x_increment + 0.5;
416 int indy = (picked.y - rect.y()) / y_increment + 0.5;
417 int pickedPointIndex = indy * wwi.width + indx;
418 if (pickedPointIndex < 0) {
419 qCDebug(KWIN_WOBBLYWINDOWS) << "Picked index == " << pickedPointIndex << " with (" << cursorPos().x() << "," << cursorPos().y() << ")";
420 pickedPointIndex = 0;
421 } else if (static_cast<unsigned int>(pickedPointIndex) > wwi.count - 1) {
422 qCDebug(KWIN_WOBBLYWINDOWS) << "Picked index == " << pickedPointIndex << " with (" << cursorPos().x() << "," << cursorPos().y() << ")";
423 pickedPointIndex = wwi.count - 1;
424 }
425#if defined VERBOSE_MODE
426 qCDebug(KWIN_WOBBLYWINDOWS) << "Original Picked point -- x : " << picked.x << " - y : " << picked.y;
427#endif
428 wwi.constraint[pickedPointIndex] = true;
429
430 if (w->isUserResize()) {
431 // on a resize, do not allow any edges to wobble until it has been moved from
432 // its original location
433 wwi.can_wobble_top = wwi.can_wobble_left = wwi.can_wobble_right = wwi.can_wobble_bottom = false;
434 wwi.resize_original_rect = w->frameGeometry();
435 } else {
436 wwi.can_wobble_top = wwi.can_wobble_left = wwi.can_wobble_right = wwi.can_wobble_bottom = true;
437 }
438}
439
440void WobblyWindowsEffect::stepMovedResized(EffectWindow *w)
441{
442 QRectF new_geometry = w->frameGeometry();
443 if (!windows.contains(w)) {
444 WindowWobblyInfos new_wwi;
445 initWobblyInfo(new_wwi, new_geometry);
446 windows[w] = new_wwi;
447 }
448
449 WindowWobblyInfos &wwi = windows[w];
450 wwi.status = Free;
451
452 QRectF maximized_area = effects->clientArea(MaximizeArea, w);
453 bool throb_direction_out = (new_geometry.top() == maximized_area.top() && new_geometry.bottom() == maximized_area.bottom()) || (new_geometry.left() == maximized_area.left() && new_geometry.right() == maximized_area.right());
454 qreal magnitude = throb_direction_out ? 10 : -30; // a small throb out when maximized, a larger throb inwards when restored
455 for (unsigned int j = 0; j < wwi.height; ++j) {
456 for (unsigned int i = 0; i < wwi.width; ++i) {
457 Pair v = {magnitude * (i / qreal(wwi.width - 1) - 0.5), magnitude * (j / qreal(wwi.height - 1) - 0.5)};
458 wwi.velocity[j * wwi.width + i] = v;
459 }
460 }
461
462 // constrain the middle of the window, so that any asymetry wont cause it to drift off-center
463 for (unsigned int j = 1; j < wwi.height - 1; ++j) {
464 for (unsigned int i = 1; i < wwi.width - 1; ++i) {
465 wwi.constraint[j * wwi.width + i] = true;
466 }
467 }
468}
469
470void WobblyWindowsEffect::initWobblyInfo(WindowWobblyInfos &wwi, QRectF geometry) const
471{
472 wwi.count = 4 * 4;
473 wwi.width = 4;
474 wwi.height = 4;
475
476 wwi.bezierWidth = m_xTesselation;
477 wwi.bezierHeight = m_yTesselation;
478 wwi.bezierCount = m_xTesselation * m_yTesselation;
479
480 wwi.origin.resize(wwi.count);
481 wwi.position.resize(wwi.count);
482 wwi.velocity.resize(wwi.count);
483 wwi.acceleration.resize(wwi.count);
484 wwi.buffer.resize(wwi.count);
485 wwi.constraint.resize(wwi.count);
486
487 wwi.bezierSurface.resize(wwi.bezierCount);
488
489 wwi.status = Moving;
490 wwi.clock = std::chrono::duration_cast<std::chrono::milliseconds>(
491 std::chrono::steady_clock::now().time_since_epoch());
492
493 qreal x = geometry.x(), y = geometry.y();
494 qreal width = geometry.width(), height = geometry.height();
495
496 Pair initValue = {x, y};
497 static const Pair nullPair = {0.0, 0.0};
498
499 qreal x_increment = width / (wwi.width - 1.0);
500 qreal y_increment = height / (wwi.height - 1.0);
501
502 for (unsigned int j = 0; j < 4; ++j) {
503 for (unsigned int i = 0; i < 4; ++i) {
504 unsigned int idx = j * 4 + i;
505 wwi.origin[idx] = initValue;
506 wwi.position[idx] = initValue;
507 wwi.velocity[idx] = nullPair;
508 wwi.constraint[idx] = false;
509 if (i != 4 - 2) { // x grid count - 2, i.e. not the last point
510 initValue.x += x_increment;
511 } else {
512 initValue.x = width + x;
513 }
514 initValue.x = initValue.x;
515 }
516 initValue.x = x;
517 initValue.x = initValue.x;
518 if (j != 4 - 2) { // y grid count - 2, i.e. not the last point
519 initValue.y += y_increment;
520 } else {
521 initValue.y = height + y;
522 }
523 initValue.y = initValue.y;
524 }
525}
526
527WobblyWindowsEffect::Pair WobblyWindowsEffect::computeBezierPoint(const WindowWobblyInfos &wwi, Pair point) const
528{
529 const qreal tx = point.x;
530 const qreal ty = point.y;
531
532 // compute polynomial coeff
533
534 qreal px[4];
535 px[0] = (1 - tx) * (1 - tx) * (1 - tx);
536 px[1] = 3 * (1 - tx) * (1 - tx) * tx;
537 px[2] = 3 * (1 - tx) * tx * tx;
538 px[3] = tx * tx * tx;
539
540 qreal py[4];
541 py[0] = (1 - ty) * (1 - ty) * (1 - ty);
542 py[1] = 3 * (1 - ty) * (1 - ty) * ty;
543 py[2] = 3 * (1 - ty) * ty * ty;
544 py[3] = ty * ty * ty;
545
546 Pair res = {0.0, 0.0};
547
548 for (unsigned int j = 0; j < 4; ++j) {
549 for (unsigned int i = 0; i < 4; ++i) {
550 // this assume the grid is 4*4
551 res.x += px[i] * py[j] * wwi.position[i + j * wwi.width].x;
552 res.y += px[i] * py[j] * wwi.position[i + j * wwi.width].y;
553 }
554 }
555
556 return res;
557}
558
559namespace
560{
561
562static inline void fixVectorBounds(WobblyWindowsEffect::Pair &vec, qreal min, qreal max)
563{
564 if (fabs(vec.x) < min) {
565 vec.x = 0.0;
566 } else if (fabs(vec.x) > max) {
567 if (vec.x > 0.0) {
568 vec.x = max;
569 } else {
570 vec.x = -max;
571 }
572 }
573
574 if (fabs(vec.y) < min) {
575 vec.y = 0.0;
576 } else if (fabs(vec.y) > max) {
577 if (vec.y > 0.0) {
578 vec.y = max;
579 } else {
580 vec.y = -max;
581 }
582 }
583}
584
585#if defined COMPUTE_STATS
586static inline void computeVectorBounds(WobblyWindowsEffect::Pair &vec, WobblyWindowsEffect::Pair &bound)
587{
588 if (fabs(vec.x) < bound.x) {
589 bound.x = fabs(vec.x);
590 } else if (fabs(vec.x) > bound.y) {
591 bound.y = fabs(vec.x);
592 }
593 if (fabs(vec.y) < bound.x) {
594 bound.x = fabs(vec.y);
595 } else if (fabs(vec.y) > bound.y) {
596 bound.y = fabs(vec.y);
597 }
598}
599#endif
600
601} // close the anonymous namespace
602
603bool WobblyWindowsEffect::updateWindowWobblyDatas(EffectWindow *w, qreal time)
604{
605 QRectF rect = w->frameGeometry();
606 WindowWobblyInfos &wwi = windows[w];
607
608 qreal x_length = rect.width() / (wwi.width - 1.0);
609 qreal y_length = rect.height() / (wwi.height - 1.0);
610
611#if defined VERBOSE_MODE
612 qCDebug(KWIN_WOBBLYWINDOWS) << "time " << time;
613 qCDebug(KWIN_WOBBLYWINDOWS) << "increment x " << x_length << " // y" << y_length;
614#endif
615
616 Pair origine = {rect.x(), rect.y()};
617
618 for (unsigned int j = 0; j < wwi.height; ++j) {
619 for (unsigned int i = 0; i < wwi.width; ++i) {
620 wwi.origin[wwi.width * j + i] = origine;
621 if (i != wwi.width - 2) {
622 origine.x += x_length;
623 } else {
624 origine.x = rect.width() + rect.x();
625 }
626 }
627 origine.x = rect.x();
628 if (j != wwi.height - 2) {
629 origine.y += y_length;
630 } else {
631 origine.y = rect.height() + rect.y();
632 }
633 }
634
635 Pair neibourgs[4];
636 Pair acceleration;
637
638 qreal acc_sum = 0.0;
639 qreal vel_sum = 0.0;
640
641 // compute acceleration, velocity and position for each point
642
643 // for corners
644
645 // top-left
646
647 if (wwi.constraint[0]) {
648 Pair window_pos = wwi.origin[0];
649 Pair current_pos = wwi.position[0];
650 Pair move = {window_pos.x - current_pos.x, window_pos.y - current_pos.y};
651 Pair accel = {move.x * m_stiffness, move.y * m_stiffness};
652 wwi.acceleration[0] = accel;
653 } else {
654 Pair &pos = wwi.position[0];
655 neibourgs[0] = wwi.position[1];
656 neibourgs[1] = wwi.position[wwi.width];
657
658 acceleration.x = ((neibourgs[0].x - pos.x) - x_length) * m_stiffness + (neibourgs[1].x - pos.x) * m_stiffness;
659 acceleration.y = ((neibourgs[1].y - pos.y) - y_length) * m_stiffness + (neibourgs[0].y - pos.y) * m_stiffness;
660
661 acceleration.x /= 2;
662 acceleration.y /= 2;
663
664 wwi.acceleration[0] = acceleration;
665 }
666
667 // top-right
668
669 if (wwi.constraint[wwi.width - 1]) {
670 Pair window_pos = wwi.origin[wwi.width - 1];
671 Pair current_pos = wwi.position[wwi.width - 1];
672 Pair move = {window_pos.x - current_pos.x, window_pos.y - current_pos.y};
673 Pair accel = {move.x * m_stiffness, move.y * m_stiffness};
674 wwi.acceleration[wwi.width - 1] = accel;
675 } else {
676 Pair &pos = wwi.position[wwi.width - 1];
677 neibourgs[0] = wwi.position[wwi.width - 2];
678 neibourgs[1] = wwi.position[2 * wwi.width - 1];
679
680 acceleration.x = (x_length - (pos.x - neibourgs[0].x)) * m_stiffness + (neibourgs[1].x - pos.x) * m_stiffness;
681 acceleration.y = ((neibourgs[1].y - pos.y) - y_length) * m_stiffness + (neibourgs[0].y - pos.y) * m_stiffness;
682
683 acceleration.x /= 2;
684 acceleration.y /= 2;
685
686 wwi.acceleration[wwi.width - 1] = acceleration;
687 }
688
689 // bottom-left
690
691 if (wwi.constraint[wwi.width * (wwi.height - 1)]) {
692 Pair window_pos = wwi.origin[wwi.width * (wwi.height - 1)];
693 Pair current_pos = wwi.position[wwi.width * (wwi.height - 1)];
694 Pair move = {window_pos.x - current_pos.x, window_pos.y - current_pos.y};
695 Pair accel = {move.x * m_stiffness, move.y * m_stiffness};
696 wwi.acceleration[wwi.width * (wwi.height - 1)] = accel;
697 } else {
698 Pair &pos = wwi.position[wwi.width * (wwi.height - 1)];
699 neibourgs[0] = wwi.position[wwi.width * (wwi.height - 1) + 1];
700 neibourgs[1] = wwi.position[wwi.width * (wwi.height - 2)];
701
702 acceleration.x = ((neibourgs[0].x - pos.x) - x_length) * m_stiffness + (neibourgs[1].x - pos.x) * m_stiffness;
703 acceleration.y = (y_length - (pos.y - neibourgs[1].y)) * m_stiffness + (neibourgs[0].y - pos.y) * m_stiffness;
704
705 acceleration.x /= 2;
706 acceleration.y /= 2;
707
708 wwi.acceleration[wwi.width * (wwi.height - 1)] = acceleration;
709 }
710
711 // bottom-right
712
713 if (wwi.constraint[wwi.count - 1]) {
714 Pair window_pos = wwi.origin[wwi.count - 1];
715 Pair current_pos = wwi.position[wwi.count - 1];
716 Pair move = {window_pos.x - current_pos.x, window_pos.y - current_pos.y};
717 Pair accel = {move.x * m_stiffness, move.y * m_stiffness};
718 wwi.acceleration[wwi.count - 1] = accel;
719 } else {
720 Pair &pos = wwi.position[wwi.count - 1];
721 neibourgs[0] = wwi.position[wwi.count - 2];
722 neibourgs[1] = wwi.position[wwi.width * (wwi.height - 1) - 1];
723
724 acceleration.x = (x_length - (pos.x - neibourgs[0].x)) * m_stiffness + (neibourgs[1].x - pos.x) * m_stiffness;
725 acceleration.y = (y_length - (pos.y - neibourgs[1].y)) * m_stiffness + (neibourgs[0].y - pos.y) * m_stiffness;
726
727 acceleration.x /= 2;
728 acceleration.y /= 2;
729
730 wwi.acceleration[wwi.count - 1] = acceleration;
731 }
732
733 // for borders
734
735 // top border
736 for (unsigned int i = 1; i < wwi.width - 1; ++i) {
737 if (wwi.constraint[i]) {
738 Pair window_pos = wwi.origin[i];
739 Pair current_pos = wwi.position[i];
740 Pair move = {window_pos.x - current_pos.x, window_pos.y - current_pos.y};
741 Pair accel = {move.x * m_stiffness, move.y * m_stiffness};
742 wwi.acceleration[i] = accel;
743 } else {
744 Pair &pos = wwi.position[i];
745 neibourgs[0] = wwi.position[i - 1];
746 neibourgs[1] = wwi.position[i + 1];
747 neibourgs[2] = wwi.position[i + wwi.width];
748
749 acceleration.x = (x_length - (pos.x - neibourgs[0].x)) * m_stiffness + ((neibourgs[1].x - pos.x) - x_length) * m_stiffness + (neibourgs[2].x - pos.x) * m_stiffness;
750 acceleration.y = ((neibourgs[2].y - pos.y) - y_length) * m_stiffness + (neibourgs[0].y - pos.y) * m_stiffness + (neibourgs[1].y - pos.y) * m_stiffness;
751
752 acceleration.x /= 3;
753 acceleration.y /= 3;
754
755 wwi.acceleration[i] = acceleration;
756 }
757 }
758
759 // bottom border
760 for (unsigned int i = wwi.width * (wwi.height - 1) + 1; i < wwi.count - 1; ++i) {
761 if (wwi.constraint[i]) {
762 Pair window_pos = wwi.origin[i];
763 Pair current_pos = wwi.position[i];
764 Pair move = {window_pos.x - current_pos.x, window_pos.y - current_pos.y};
765 Pair accel = {move.x * m_stiffness, move.y * m_stiffness};
766 wwi.acceleration[i] = accel;
767 } else {
768 Pair &pos = wwi.position[i];
769 neibourgs[0] = wwi.position[i - 1];
770 neibourgs[1] = wwi.position[i + 1];
771 neibourgs[2] = wwi.position[i - wwi.width];
772
773 acceleration.x = (x_length - (pos.x - neibourgs[0].x)) * m_stiffness + ((neibourgs[1].x - pos.x) - x_length) * m_stiffness + (neibourgs[2].x - pos.x) * m_stiffness;
774 acceleration.y = (y_length - (pos.y - neibourgs[2].y)) * m_stiffness + (neibourgs[0].y - pos.y) * m_stiffness + (neibourgs[1].y - pos.y) * m_stiffness;
775
776 acceleration.x /= 3;
777 acceleration.y /= 3;
778
779 wwi.acceleration[i] = acceleration;
780 }
781 }
782
783 // left border
784 for (unsigned int i = wwi.width; i < wwi.width * (wwi.height - 1); i += wwi.width) {
785 if (wwi.constraint[i]) {
786 Pair window_pos = wwi.origin[i];
787 Pair current_pos = wwi.position[i];
788 Pair move = {window_pos.x - current_pos.x, window_pos.y - current_pos.y};
789 Pair accel = {move.x * m_stiffness, move.y * m_stiffness};
790 wwi.acceleration[i] = accel;
791 } else {
792 Pair &pos = wwi.position[i];
793 neibourgs[0] = wwi.position[i + 1];
794 neibourgs[1] = wwi.position[i - wwi.width];
795 neibourgs[2] = wwi.position[i + wwi.width];
796
797 acceleration.x = ((neibourgs[0].x - pos.x) - x_length) * m_stiffness + (neibourgs[1].x - pos.x) * m_stiffness + (neibourgs[2].x - pos.x) * m_stiffness;
798 acceleration.y = (y_length - (pos.y - neibourgs[1].y)) * m_stiffness + ((neibourgs[2].y - pos.y) - y_length) * m_stiffness + (neibourgs[0].y - pos.y) * m_stiffness;
799
800 acceleration.x /= 3;
801 acceleration.y /= 3;
802
803 wwi.acceleration[i] = acceleration;
804 }
805 }
806
807 // right border
808 for (unsigned int i = 2 * wwi.width - 1; i < wwi.count - 1; i += wwi.width) {
809 if (wwi.constraint[i]) {
810 Pair window_pos = wwi.origin[i];
811 Pair current_pos = wwi.position[i];
812 Pair move = {window_pos.x - current_pos.x, window_pos.y - current_pos.y};
813 Pair accel = {move.x * m_stiffness, move.y * m_stiffness};
814 wwi.acceleration[i] = accel;
815 } else {
816 Pair &pos = wwi.position[i];
817 neibourgs[0] = wwi.position[i - 1];
818 neibourgs[1] = wwi.position[i - wwi.width];
819 neibourgs[2] = wwi.position[i + wwi.width];
820
821 acceleration.x = (x_length - (pos.x - neibourgs[0].x)) * m_stiffness + (neibourgs[1].x - pos.x) * m_stiffness + (neibourgs[2].x - pos.x) * m_stiffness;
822 acceleration.y = (y_length - (pos.y - neibourgs[1].y)) * m_stiffness + ((neibourgs[2].y - pos.y) - y_length) * m_stiffness + (neibourgs[0].y - pos.y) * m_stiffness;
823
824 acceleration.x /= 3;
825 acceleration.y /= 3;
826
827 wwi.acceleration[i] = acceleration;
828 }
829 }
830
831 // for the inner points
832 for (unsigned int j = 1; j < wwi.height - 1; ++j) {
833 for (unsigned int i = 1; i < wwi.width - 1; ++i) {
834 unsigned int index = i + j * wwi.width;
835
836 if (wwi.constraint[index]) {
837 Pair window_pos = wwi.origin[index];
838 Pair current_pos = wwi.position[index];
839 Pair move = {window_pos.x - current_pos.x, window_pos.y - current_pos.y};
840 Pair accel = {move.x * m_stiffness, move.y * m_stiffness};
841 wwi.acceleration[index] = accel;
842 } else {
843 Pair &pos = wwi.position[index];
844 neibourgs[0] = wwi.position[index - 1];
845 neibourgs[1] = wwi.position[index + 1];
846 neibourgs[2] = wwi.position[index - wwi.width];
847 neibourgs[3] = wwi.position[index + wwi.width];
848
849 acceleration.x = ((neibourgs[0].x - pos.x) - x_length) * m_stiffness + (x_length - (pos.x - neibourgs[1].x)) * m_stiffness + (neibourgs[2].x - pos.x) * m_stiffness + (neibourgs[3].x - pos.x) * m_stiffness;
850 acceleration.y = (y_length - (pos.y - neibourgs[2].y)) * m_stiffness + ((neibourgs[3].y - pos.y) - y_length) * m_stiffness + (neibourgs[0].y - pos.y) * m_stiffness + (neibourgs[1].y - pos.y) * m_stiffness;
851
852 acceleration.x /= 4;
853 acceleration.y /= 4;
854
855 wwi.acceleration[index] = acceleration;
856 }
857 }
858 }
859
860 heightRingLinearMean(wwi.acceleration, wwi);
861
862#if defined COMPUTE_STATS
863 Pair accBound = {m_maxAcceleration, m_minAcceleration};
864 Pair velBound = {m_maxVelocity, m_minVelocity};
865#endif
866
867 // compute the new velocity of each vertex.
868 for (unsigned int i = 0; i < wwi.count; ++i) {
869 Pair acc = wwi.acceleration[i];
870 fixVectorBounds(acc, m_minAcceleration, m_maxAcceleration);
871
872#if defined COMPUTE_STATS
873 computeVectorBounds(acc, accBound);
874#endif
875
876 Pair &vel = wwi.velocity[i];
877 vel.x = acc.x * time + vel.x * m_drag;
878 vel.y = acc.y * time + vel.y * m_drag;
879
880 acc_sum += fabs(acc.x) + fabs(acc.y);
881 }
882
883 heightRingLinearMean(wwi.velocity, wwi);
884
885 // compute the new pos of each vertex.
886 for (unsigned int i = 0; i < wwi.count; ++i) {
887 Pair &pos = wwi.position[i];
888 Pair &vel = wwi.velocity[i];
889
890 fixVectorBounds(vel, m_minVelocity, m_maxVelocity);
891#if defined COMPUTE_STATS
892 computeVectorBounds(vel, velBound);
893#endif
894
895 pos.x += vel.x * time * m_move_factor;
896 pos.y += vel.y * time * m_move_factor;
897
898 vel_sum += fabs(vel.x) + fabs(vel.y);
899
900#if defined VERBOSE_MODE
901 if (wwi.constraint[i]) {
902 qCDebug(KWIN_WOBBLYWINDOWS) << "Constraint point ** vel : " << vel.x << "," << vel.y << " ** move : " << vel.x * time << "," << vel.y * time;
903 }
904#endif
905 }
906
907 if (!wwi.can_wobble_top) {
908 for (unsigned int i = 0; i < wwi.width; ++i) {
909 for (unsigned j = 0; j < wwi.width - 1; ++j) {
910 wwi.position[i + wwi.width * j].y = wwi.origin[i + wwi.width * j].y;
911 }
912 }
913 }
914 if (!wwi.can_wobble_bottom) {
915 for (unsigned int i = wwi.width * (wwi.height - 1); i < wwi.count; ++i) {
916 for (unsigned j = 0; j < wwi.width - 1; ++j) {
917 wwi.position[i - wwi.width * j].y = wwi.origin[i - wwi.width * j].y;
918 }
919 }
920 }
921 if (!wwi.can_wobble_left) {
922 for (unsigned int i = 0; i < wwi.count; i += wwi.width) {
923 for (unsigned j = 0; j < wwi.width - 1; ++j) {
924 wwi.position[i + j].x = wwi.origin[i + j].x;
925 }
926 }
927 }
928 if (!wwi.can_wobble_right) {
929 for (unsigned int i = wwi.width - 1; i < wwi.count; i += wwi.width) {
930 for (unsigned j = 0; j < wwi.width - 1; ++j) {
931 wwi.position[i - j].x = wwi.origin[i - j].x;
932 }
933 }
934 }
935
936#if defined VERBOSE_MODE
937#if defined COMPUTE_STATS
938 qCDebug(KWIN_WOBBLYWINDOWS) << "Acceleration bounds (" << accBound.x << ", " << accBound.y << ")";
939 qCDebug(KWIN_WOBBLYWINDOWS) << "Velocity bounds (" << velBound.x << ", " << velBound.y << ")";
940#endif
941 qCDebug(KWIN_WOBBLYWINDOWS) << "sum_acc : " << acc_sum << " *** sum_vel :" << vel_sum;
942#endif
943
944 wwi.wobblying = !(acc_sum < m_stopAcceleration && vel_sum < m_stopVelocity);
945 if (wwi.status != Moving && !wwi.wobblying) {
946 windows.remove(w);
947 unredirect(w);
948 if (windows.isEmpty()) {
950 }
951 return false;
952 } else if (!wwi.wobblying) {
954 }
955
956 return true;
957}
958
959void WobblyWindowsEffect::heightRingLinearMean(QList<Pair> &data, WindowWobblyInfos &wwi)
960{
961 Pair neibourgs[8];
962
963 // for corners
964
965 // top-left
966 {
967 Pair &res = wwi.buffer[0];
968 Pair vit = data[0];
969 neibourgs[0] = data[1];
970 neibourgs[1] = data[wwi.width];
971 neibourgs[2] = data[wwi.width + 1];
972
973 res.x = (neibourgs[0].x + neibourgs[1].x + neibourgs[2].x + 3.0 * vit.x) / 6.0;
974 res.y = (neibourgs[0].y + neibourgs[1].y + neibourgs[2].y + 3.0 * vit.y) / 6.0;
975 }
976
977 // top-right
978 {
979 Pair &res = wwi.buffer[wwi.width - 1];
980 Pair vit = data[wwi.width - 1];
981 neibourgs[0] = data[wwi.width - 2];
982 neibourgs[1] = data[2 * wwi.width - 1];
983 neibourgs[2] = data[2 * wwi.width - 2];
984
985 res.x = (neibourgs[0].x + neibourgs[1].x + neibourgs[2].x + 3.0 * vit.x) / 6.0;
986 res.y = (neibourgs[0].y + neibourgs[1].y + neibourgs[2].y + 3.0 * vit.y) / 6.0;
987 }
988
989 // bottom-left
990 {
991 Pair &res = wwi.buffer[wwi.width * (wwi.height - 1)];
992 Pair vit = data[wwi.width * (wwi.height - 1)];
993 neibourgs[0] = data[wwi.width * (wwi.height - 1) + 1];
994 neibourgs[1] = data[wwi.width * (wwi.height - 2)];
995 neibourgs[2] = data[wwi.width * (wwi.height - 2) + 1];
996
997 res.x = (neibourgs[0].x + neibourgs[1].x + neibourgs[2].x + 3.0 * vit.x) / 6.0;
998 res.y = (neibourgs[0].y + neibourgs[1].y + neibourgs[2].y + 3.0 * vit.y) / 6.0;
999 }
1000
1001 // bottom-right
1002 {
1003 Pair &res = wwi.buffer[wwi.count - 1];
1004 Pair vit = data[wwi.count - 1];
1005 neibourgs[0] = data[wwi.count - 2];
1006 neibourgs[1] = data[wwi.width * (wwi.height - 1) - 1];
1007 neibourgs[2] = data[wwi.width * (wwi.height - 1) - 2];
1008
1009 res.x = (neibourgs[0].x + neibourgs[1].x + neibourgs[2].x + 3.0 * vit.x) / 6.0;
1010 res.y = (neibourgs[0].y + neibourgs[1].y + neibourgs[2].y + 3.0 * vit.y) / 6.0;
1011 }
1012
1013 // for borders
1014
1015 // top border
1016 for (unsigned int i = 1; i < wwi.width - 1; ++i) {
1017 Pair &res = wwi.buffer[i];
1018 Pair vit = data[i];
1019 neibourgs[0] = data[i - 1];
1020 neibourgs[1] = data[i + 1];
1021 neibourgs[2] = data[i + wwi.width];
1022 neibourgs[3] = data[i + wwi.width - 1];
1023 neibourgs[4] = data[i + wwi.width + 1];
1024
1025 res.x = (neibourgs[0].x + neibourgs[1].x + neibourgs[2].x + neibourgs[3].x + neibourgs[4].x + 5.0 * vit.x) / 10.0;
1026 res.y = (neibourgs[0].y + neibourgs[1].y + neibourgs[2].y + neibourgs[3].y + neibourgs[4].y + 5.0 * vit.y) / 10.0;
1027 }
1028
1029 // bottom border
1030 for (unsigned int i = wwi.width * (wwi.height - 1) + 1; i < wwi.count - 1; ++i) {
1031 Pair &res = wwi.buffer[i];
1032 Pair vit = data[i];
1033 neibourgs[0] = data[i - 1];
1034 neibourgs[1] = data[i + 1];
1035 neibourgs[2] = data[i - wwi.width];
1036 neibourgs[3] = data[i - wwi.width - 1];
1037 neibourgs[4] = data[i - wwi.width + 1];
1038
1039 res.x = (neibourgs[0].x + neibourgs[1].x + neibourgs[2].x + neibourgs[3].x + neibourgs[4].x + 5.0 * vit.x) / 10.0;
1040 res.y = (neibourgs[0].y + neibourgs[1].y + neibourgs[2].y + neibourgs[3].y + neibourgs[4].y + 5.0 * vit.y) / 10.0;
1041 }
1042
1043 // left border
1044 for (unsigned int i = wwi.width; i < wwi.width * (wwi.height - 1); i += wwi.width) {
1045 Pair &res = wwi.buffer[i];
1046 Pair vit = data[i];
1047 neibourgs[0] = data[i + 1];
1048 neibourgs[1] = data[i - wwi.width];
1049 neibourgs[2] = data[i + wwi.width];
1050 neibourgs[3] = data[i - wwi.width + 1];
1051 neibourgs[4] = data[i + wwi.width + 1];
1052
1053 res.x = (neibourgs[0].x + neibourgs[1].x + neibourgs[2].x + neibourgs[3].x + neibourgs[4].x + 5.0 * vit.x) / 10.0;
1054 res.y = (neibourgs[0].y + neibourgs[1].y + neibourgs[2].y + neibourgs[3].y + neibourgs[4].y + 5.0 * vit.y) / 10.0;
1055 }
1056
1057 // right border
1058 for (unsigned int i = 2 * wwi.width - 1; i < wwi.count - 1; i += wwi.width) {
1059 Pair &res = wwi.buffer[i];
1060 Pair vit = data[i];
1061 neibourgs[0] = data[i - 1];
1062 neibourgs[1] = data[i - wwi.width];
1063 neibourgs[2] = data[i + wwi.width];
1064 neibourgs[3] = data[i - wwi.width - 1];
1065 neibourgs[4] = data[i + wwi.width - 1];
1066
1067 res.x = (neibourgs[0].x + neibourgs[1].x + neibourgs[2].x + neibourgs[3].x + neibourgs[4].x + 5.0 * vit.x) / 10.0;
1068 res.y = (neibourgs[0].y + neibourgs[1].y + neibourgs[2].y + neibourgs[3].y + neibourgs[4].y + 5.0 * vit.y) / 10.0;
1069 }
1070
1071 // for the inner points
1072 for (unsigned int j = 1; j < wwi.height - 1; ++j) {
1073 for (unsigned int i = 1; i < wwi.width - 1; ++i) {
1074 unsigned int index = i + j * wwi.width;
1075
1076 Pair &res = wwi.buffer[index];
1077 Pair &vit = data[index];
1078 neibourgs[0] = data[index - 1];
1079 neibourgs[1] = data[index + 1];
1080 neibourgs[2] = data[index - wwi.width];
1081 neibourgs[3] = data[index + wwi.width];
1082 neibourgs[4] = data[index - wwi.width - 1];
1083 neibourgs[5] = data[index - wwi.width + 1];
1084 neibourgs[6] = data[index + wwi.width - 1];
1085 neibourgs[7] = data[index + wwi.width + 1];
1086
1087 res.x = (neibourgs[0].x + neibourgs[1].x + neibourgs[2].x + neibourgs[3].x + neibourgs[4].x + neibourgs[5].x + neibourgs[6].x + neibourgs[7].x + 8.0 * vit.x) / 16.0;
1088 res.y = (neibourgs[0].y + neibourgs[1].y + neibourgs[2].y + neibourgs[3].y + neibourgs[4].y + neibourgs[5].y + neibourgs[6].y + neibourgs[7].y + 8.0 * vit.y) / 16.0;
1089 }
1090 }
1091
1092 auto tmp = data;
1093 data = wwi.buffer;
1094 wwi.buffer = tmp;
1095}
1096
1098{
1099 return !windows.isEmpty();
1100}
1101
1103{
1104 return m_stiffness;
1105}
1106
1108{
1109 return m_drag;
1110}
1111
1113{
1114 return m_move_factor;
1115}
1116
1118{
1119 return m_xTesselation;
1120}
1121
1123{
1124 return m_yTesselation;
1125}
1126
1128{
1129 return m_minVelocity;
1130}
1131
1133{
1134 return m_maxVelocity;
1135}
1136
1138{
1139 return m_stopVelocity;
1140}
1141
1143{
1144 return m_minAcceleration;
1145}
1146
1148{
1149 return m_maxAcceleration;
1150}
1151
1153{
1154 return m_stopAcceleration;
1155}
1156
1158{
1159 return m_moveWobble;
1160}
1161
1163{
1164 return m_resizeWobble;
1165}
1166
1167} // namespace KWin
1168
1169#include "moc_wobblywindows.cpp"
Representation of a window used by/for Effect classes.
void windowStartUserMovedResized(KWin::EffectWindow *w)
bool isUserResize() const
void windowStepUserMovedResized(KWin::EffectWindow *w, const QRectF &geometry)
bool isSpecialWindow() const
bool isUserMove() const
void windowFinishUserMovedResized(KWin::EffectWindow *w)
void windowMaximizedStateChanged(KWin::EffectWindow *w, bool horizontal, bool vertical)
QRectF frameGeometry() const
bool animationsSupported() const
QList< EffectWindow * > stackingOrder
Q_SCRIPTABLE void addRepaint(const QRectF &r)
void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
void prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime)
QRectF clientArea(clientAreaOption, const Output *screen, const VirtualDesktop *desktop) const
KSharedConfigPtr config() const
void windowAdded(KWin::EffectWindow *w)
Q_SCRIPTABLE void addRepaintFull()
void unredirect(EffectWindow *window)
void redirect(EffectWindow *window)
void setVertexSnappingMode(RenderGeometry::VertexSnappingMode mode)
WindowQuadList makeRegularGrid(int xSubdivisions, int ySubdivisions) const
Vertex class.
double x() const
void move(double x, double y)
double y() const
void slotWindowStepUserMovedResized(KWin::EffectWindow *w, const QRectF &geometry)
void apply(EffectWindow *w, int mask, WindowPaintData &data, WindowQuadList &quads) override
void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) override
void setMoveFactor(qreal factor)
void slotWindowAdded(KWin::EffectWindow *w)
void slotWindowStartUserMovedResized(KWin::EffectWindow *w)
void setVelocityThreshold(qreal velocityThreshold)
void postPaintScreen() override
bool isActive() const override
void slotWindowFinishUserMovedResized(KWin::EffectWindow *w)
void slotWindowMaximizeStateChanged(KWin::EffectWindow *w, bool horizontal, bool vertical)
void prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime) override
void reconfigure(ReconfigureFlags) override
void setStiffness(qreal stiffness)
static QPointF cursorPos()
Definition effect.cpp:478
qreal yTranslation() const
Definition effect.cpp:131
qreal yScale() const
Definition effect.cpp:61
qreal xScale() const
Definition effect.cpp:56
qreal xTranslation() const
Definition effect.cpp:126
@ PAINT_SCREEN_TRANSFORMED
Definition effect.h:562
@ ReconfigureAll
Definition effect.h:601
@ MaximizeArea
Definition globals.h:51
EffectsHandler * effects