Changeset View
Changeset View
Standalone View
Standalone View
shell_client.cpp
Show First 20 Lines • Show All 48 Lines • ▼ Show 20 Line(s) | |||||
49 | #include <KWayland/Server/appmenu_interface.h> | 49 | #include <KWayland/Server/appmenu_interface.h> | ||
50 | #include <KWayland/Server/server_decoration_palette_interface.h> | 50 | #include <KWayland/Server/server_decoration_palette_interface.h> | ||
51 | 51 | | |||
52 | #include <KDesktopFile> | 52 | #include <KDesktopFile> | ||
53 | 53 | | |||
54 | #include <QFileInfo> | 54 | #include <QFileInfo> | ||
55 | #include <QOpenGLFramebufferObject> | 55 | #include <QOpenGLFramebufferObject> | ||
56 | #include <QWindow> | 56 | #include <QWindow> | ||
57 | #include <QScopedValueRollback> | ||||
57 | 58 | | |||
58 | #include <sys/types.h> | 59 | #include <sys/types.h> | ||
59 | #include <unistd.h> | 60 | #include <unistd.h> | ||
60 | #include <signal.h> | 61 | #include <signal.h> | ||
61 | 62 | | |||
62 | using namespace KWayland::Server; | 63 | using namespace KWayland::Server; | ||
63 | 64 | | |||
64 | static const QByteArray s_skipClosePropertyName = QByteArrayLiteral("KWIN_SKIP_CLOSE_ANIMATION"); | 65 | static const QByteArray s_skipClosePropertyName = QByteArrayLiteral("KWIN_SKIP_CLOSE_ANIMATION"); | ||
▲ Show 20 Lines • Show All 1508 Lines • ▼ Show 20 Line(s) | |||||
1573 | } | 1574 | } | ||
1574 | 1575 | | |||
1575 | bool ShellClient::hasTransientPlacementHint() const | 1576 | bool ShellClient::hasTransientPlacementHint() const | ||
1576 | { | 1577 | { | ||
1577 | return isTransient() && transientFor() != nullptr && | 1578 | return isTransient() && transientFor() != nullptr && | ||
1578 | (m_shellSurface || m_xdgShellPopup); | 1579 | (m_shellSurface || m_xdgShellPopup); | ||
1579 | } | 1580 | } | ||
1580 | 1581 | | |||
1581 | QPoint ShellClient::transientPlacementHint() const | 1582 | QRect ShellClient::transientPlacement(const QRect &bounds) const | ||
1582 | { | 1583 | { | ||
1584 | QRect anchorRect; | ||||
1585 | Qt::Edges anchorEdge; | ||||
1586 | Qt::Edges gravity; | ||||
1587 | QPoint offset; | ||||
1588 | PositionerConstraints constraintAdjustments; | ||||
1589 | | ||||
1590 | const QPoint parentClientPos = transientFor()->pos() + transientFor()->clientPos(); | ||||
1591 | QRect popupPosition; | ||||
1592 | | ||||
1593 | // returns if a target is within the supplied bounds, optional edges argument states which side to check | ||||
1594 | auto inBounds = [bounds](const QRect &target, Qt::Edges edges = Qt::LeftEdge | Qt::RightEdge | Qt::TopEdge | Qt::BottomEdge) -> bool { | ||||
1595 | if (edges & Qt::LeftEdge && target.left() < bounds.left()) { | ||||
1596 | return false; | ||||
1597 | } | ||||
1598 | if (edges & Qt::TopEdge && target.top() < bounds.top()) { | ||||
1599 | return false; | ||||
1600 | } | ||||
1601 | if (edges & Qt::RightEdge && target.right() > bounds.right()) { | ||||
1602 | //normal QRect::right issue cancels out | ||||
1603 | return false; | ||||
1604 | } | ||||
1605 | if (edges & Qt::BottomEdge && target.bottom() > bounds.bottom()) { | ||||
1606 | return false; | ||||
1607 | } | ||||
1608 | return true; | ||||
1609 | }; | ||||
1610 | | ||||
1583 | if (m_shellSurface) { | 1611 | if (m_shellSurface) { | ||
1584 | return m_shellSurface->transientOffset(); | 1612 | anchorRect = QRect(m_shellSurface->transientOffset(), QSize(1,1)); | ||
1613 | anchorEdge = Qt::TopEdge | Qt::LeftEdge; | ||||
1614 | gravity = Qt::BottomEdge | Qt::RightEdge; //our single point represents the top left of the popup | ||||
1615 | constraintAdjustments = (PositionerConstraint::SlideX | PositionerConstraint::SlideY); | ||||
1616 | } else if (m_xdgShellPopup) { | ||||
1617 | anchorRect = m_xdgShellPopup->anchorRect(); | ||||
1618 | anchorEdge = m_xdgShellPopup->anchorEdge(); | ||||
1619 | gravity = m_xdgShellPopup->gravity(); | ||||
1620 | offset= m_xdgShellPopup->anchorOffset(); | ||||
zzag: Missing whitespace before "=" | |||||
1621 | constraintAdjustments = m_xdgShellPopup->constraintAdjustments(); | ||||
1622 | } else { | ||||
1623 | Q_ASSERT(false); | ||||
zzag: Q_UNREACHABLE | |||||
zzag: ... or Q_ASSERT_X to print more "user"-friendly message. | |||||
This is the one of the few times where Q_UNREACHABLE actually saves a single jump instruction. May as well, given it documents the assert better. davidedmundson: This is the one of the few times where Q_UNREACHABLE actually saves a single jump instruction. | |||||
1585 | } | 1624 | } | ||
1586 | if (m_xdgShellPopup) { | 1625 | | ||
1587 | return m_xdgShellPopup->transientOffset(); | 1626 | //initial position | ||
1627 | popupPosition = QRect(popupOffset(anchorRect, anchorEdge, gravity) + offset + parentClientPos, geometry().size()); | ||||
1628 | | ||||
1629 | //if that fits, we don't need to do anything | ||||
1630 | if (inBounds(popupPosition)) { | ||||
1631 | return popupPosition; | ||||
1632 | } | ||||
1633 | //otherwise apply constraint adjustment per axis in order XDG Shell Popup states | ||||
1634 | | ||||
1635 | if (constraintAdjustments & PositionerConstraint::SlideX) { | ||||
1636 | if (!inBounds(popupPosition, Qt::LeftEdge)) { | ||||
1637 | popupPosition.setX(bounds.x()); | ||||
1638 | } | ||||
1639 | if (!inBounds(popupPosition, Qt::RightEdge)) { | ||||
1640 | popupPosition.setX(bounds.x() + bounds.width() - geometry().width()); | ||||
1641 | } | ||||
1642 | } | ||||
1643 | if (constraintAdjustments & PositionerConstraint::FlipX) { | ||||
zzag: Aren't FlipX and FlipY having precedence over SlideX and SlideY respectively? | |||||
davidedmundson: heh, the spec lists slide first in the enum
But yeah, we should. | |||||
1644 | if (!inBounds(popupPosition, Qt::LeftEdge | Qt::RightEdge)) { | ||||
1645 | //flip both edges (if either bit is set, XOR both) | ||||
1646 | auto flippedAnchorEdge = anchorEdge; | ||||
1647 | if (flippedAnchorEdge & (Qt::LeftEdge | Qt::RightEdge)) { | ||||
1648 | flippedAnchorEdge ^= (Qt::LeftEdge | Qt::RightEdge); | ||||
1649 | } | ||||
1650 | auto flippedGravity = gravity; | ||||
1651 | if (flippedGravity & (Qt::LeftEdge | Qt::RightEdge)) { | ||||
1652 | flippedGravity ^= (Qt::LeftEdge | Qt::RightEdge); | ||||
1653 | } | ||||
1654 | auto flippedPopupPosition = QRect(popupOffset(anchorRect, flippedAnchorEdge, flippedGravity) + offset + parentClientPos, geometry().size()); | ||||
1655 | | ||||
1656 | //if it still doesn't fit we should resize the unflipped version | ||||
1657 | if (inBounds(flippedPopupPosition, Qt::LeftEdge | Qt::RightEdge)) { | ||||
1658 | popupPosition.setX(flippedPopupPosition.x()); | ||||
1659 | } | ||||
1660 | } | ||||
1661 | } | ||||
1662 | if (constraintAdjustments & PositionerConstraint::ResizeX) { | ||||
1663 | //TODO | ||||
1664 | //but we need to sort out when this is run as resize should only happen before first configure | ||||
1665 | } | ||||
1666 | | ||||
1667 | if (constraintAdjustments & PositionerConstraint::SlideY) { | ||||
1668 | if (!inBounds(popupPosition, Qt::TopEdge)) { | ||||
1669 | popupPosition.setY(bounds.y()); | ||||
1588 | } | 1670 | } | ||
1589 | return QPoint(); | 1671 | if (!inBounds(popupPosition, Qt::BottomEdge)) { | ||
1672 | popupPosition.setY(bounds.y() + bounds.height() - geometry().height()); | ||||
1673 | } | ||||
1674 | } | ||||
1675 | if (constraintAdjustments & PositionerConstraint::FlipY) { | ||||
1676 | if (!inBounds(popupPosition, Qt::TopEdge | Qt::BottomEdge)) { | ||||
1677 | //flip both edges (if either bit is set, XOR both) | ||||
1678 | auto flippedAnchorEdge = anchorEdge; | ||||
1679 | if (flippedAnchorEdge & (Qt::TopEdge | Qt::BottomEdge)) { | ||||
1680 | flippedAnchorEdge ^= (Qt::TopEdge | Qt::BottomEdge); | ||||
1681 | } | ||||
1682 | auto flippedGravity = gravity; | ||||
1683 | if (flippedGravity & (Qt::TopEdge | Qt::BottomEdge)) { | ||||
1684 | flippedGravity ^= (Qt::TopEdge | Qt::BottomEdge); | ||||
1685 | } | ||||
1686 | auto flippedPopupPosition = QRect(popupOffset(anchorRect, flippedAnchorEdge, flippedGravity) + offset + parentClientPos, geometry().size()); | ||||
1687 | | ||||
1688 | //if it still doesn't fit we should resize the unflipped version | ||||
1689 | if (inBounds(flippedPopupPosition, Qt::TopEdge | Qt::BottomEdge)) { | ||||
1690 | popupPosition.setY(flippedPopupPosition.y()); | ||||
1691 | } | ||||
1692 | } | ||||
1693 | } | ||||
1694 | if (constraintAdjustments & PositionerConstraint::ResizeY) { | ||||
1695 | //TODO | ||||
1696 | } | ||||
1697 | | ||||
1698 | return popupPosition; | ||||
1699 | } | ||||
1700 | | ||||
1701 | QPoint ShellClient::popupOffset(const QRect anchorRect, const Qt::Edges anchorEdge, const Qt::Edges gravity) const | ||||
1702 | { | ||||
1703 | const QSize popupSize = geometry().size(); | ||||
1704 | QPoint anchorPoint; | ||||
1705 | switch(anchorEdge & (Qt::LeftEdge | Qt::RightEdge)) { | ||||
zzag: Missing whitespace between "switch" and "(" | |||||
1706 | case Qt::LeftEdge: | ||||
1707 | anchorPoint.setX(anchorRect.x()); | ||||
1708 | break; | ||||
1709 | case Qt::RightEdge: | ||||
1710 | anchorPoint.setX(anchorRect.x() + anchorRect.width()); | ||||
1711 | break; | ||||
1712 | default: | ||||
1713 | anchorPoint.setX(qRound(anchorRect.x() + anchorRect.width() / 2.0)); | ||||
1714 | } | ||||
1715 | switch(anchorEdge & (Qt::TopEdge | Qt::BottomEdge)) { | ||||
1716 | case Qt::TopEdge: | ||||
1717 | anchorPoint.setY(anchorRect.y()); | ||||
1718 | break; | ||||
1719 | case Qt::BottomEdge: | ||||
1720 | anchorPoint.setY(anchorRect.y() + anchorRect.height()); | ||||
1721 | break; | ||||
1722 | default: | ||||
1723 | anchorPoint.setY(qRound(anchorRect.y() + anchorRect.height() / 2.0)); | ||||
1724 | } | ||||
1725 | | ||||
1726 | // calculate where the top left point of the popup will end up with the applied gravity | ||||
1727 | // gravity indicates direction. i.e if gravitating towards the top the popup's bottom edge | ||||
1728 | // will next to the anchor point | ||||
1729 | QPoint popupPosAdjust; | ||||
1730 | switch(gravity & (Qt::LeftEdge | Qt::RightEdge)) { | ||||
1731 | case Qt::LeftEdge: | ||||
1732 | popupPosAdjust.setX(-popupSize.width()); | ||||
1733 | break; | ||||
1734 | case Qt::RightEdge: | ||||
1735 | popupPosAdjust.setX(0); | ||||
1736 | break; | ||||
1737 | default: | ||||
1738 | popupPosAdjust.setX(qRound(-popupSize.width() / 2.0)); | ||||
1739 | } | ||||
1740 | switch(gravity & (Qt::TopEdge | Qt::BottomEdge)) { | ||||
1741 | case Qt::TopEdge: | ||||
1742 | popupPosAdjust.setY(-popupSize.height()); | ||||
1743 | break; | ||||
1744 | case Qt::BottomEdge: | ||||
1745 | popupPosAdjust.setY(0); | ||||
1746 | break; | ||||
1747 | default: | ||||
1748 | popupPosAdjust.setY(qRound(-popupSize.height() / 2.0)); | ||||
1749 | } | ||||
1750 | | ||||
1751 | return anchorPoint + popupPosAdjust; | ||||
1590 | } | 1752 | } | ||
1591 | 1753 | | |||
1592 | bool ShellClient::isWaitingForMoveResizeSync() const | 1754 | bool ShellClient::isWaitingForMoveResizeSync() const | ||
1593 | { | 1755 | { | ||
1594 | return !m_pendingConfigureRequests.isEmpty(); | 1756 | return !m_pendingConfigureRequests.isEmpty(); | ||
1595 | } | 1757 | } | ||
1596 | 1758 | | |||
1597 | void ShellClient::doResizeSync() | 1759 | void ShellClient::doResizeSync() | ||
▲ Show 20 Lines • Show All 189 Lines • Show Last 20 Lines |
Missing whitespace before "="