diff --git a/atoms.cpp b/atoms.cpp index 576e85f0c0..439e37ecfe 100644 --- a/atoms.cpp +++ b/atoms.cpp @@ -56,6 +56,7 @@ Atoms::Atoms() , kde_color_sheme(QByteArrayLiteral("_KDE_NET_WM_COLOR_SCHEME")) , kde_skip_close_animation(QByteArrayLiteral("_KDE_NET_WM_SKIP_CLOSE_ANIMATION")) , kde_screen_edge_show(QByteArrayLiteral("_KDE_NET_WM_SCREEN_EDGE_SHOW")) + , gtk_frame_extents(QByteArrayLiteral("_GTK_FRAME_EXTENTS")) , m_dtSmWindowInfo(QByteArrayLiteral("_DT_SM_WINDOW_INFO")) , m_motifSupport(QByteArrayLiteral("_MOTIF_WM_INFO")) , m_helpersRetrieved(false) diff --git a/atoms.h b/atoms.h index d52223504a..6badfc528e 100644 --- a/atoms.h +++ b/atoms.h @@ -65,6 +65,7 @@ public: Xcb::Atom kde_color_sheme; Xcb::Atom kde_skip_close_animation; Xcb::Atom kde_screen_edge_show; + Xcb::Atom gtk_frame_extents; /** * @internal diff --git a/client.cpp b/client.cpp index 608e6a8435..68fe122126 100644 --- a/client.cpp +++ b/client.cpp @@ -232,6 +232,7 @@ Client::Client() , m_decoInputExtent() , m_focusOutTimer(nullptr) , m_palette(QApplication::palette()) + , m_clientSideDecorated(false) { // TODO: Do all as initialization syncRequest.counter = syncRequest.alarm = XCB_NONE; @@ -739,6 +740,13 @@ void Client::updateFrameExtents() info->setFrameExtents(strut); } +void Client::detectGtkFrameExtents() +{ + Xcb::Property prop(false, m_client, atoms->gtk_frame_extents, XCB_ATOM_CARDINAL, 0, 4); + m_clientSideDecorated = !prop.isNull() && prop->type != 0; + emit clientSideDecoratedChanged(); +} + /** * Resizes the decoration, and makes sure the decoration widget gets resize event * even if the size hasn't changed. This is needed to make sure the decoration diff --git a/client.h b/client.h index 8e41e203d0..3a15cbbfd4 100644 --- a/client.h +++ b/client.h @@ -278,6 +278,11 @@ class Client * Whether the decoration is currently using an alpha channel. **/ Q_PROPERTY(bool decorationHasAlpha READ decorationHasAlpha) + /** + * Whether the Client uses client side window decorations. + * Only GTK+ are detected. + **/ + Q_PROPERTY(bool clientSideDecorated READ isClientSideDecorated NOTIFY clientSideDecoratedChanged) public: explicit Client(); xcb_window_t wrapperId() const; @@ -608,6 +613,7 @@ public: QRegion decorationPendingRegion() const; bool decorationHasAlpha() const; + bool isClientSideDecorated() const; Position titlebarPosition() const; @@ -780,6 +786,7 @@ Q_SIGNALS: * Emitted whenever the Client's block compositing state changes. **/ void blockingCompositingChanged(KWin::Client *client); + void clientSideDecoratedChanged(); private: void exportMappingState(int s); // ICCCM 4.1.3.1, 4.1.4, NETWM 2.5.1 @@ -827,6 +834,7 @@ private: void embedClient(xcb_window_t w, xcb_visualid_t visualid, xcb_colormap_t colormap, uint8_t depth); void detectNoBorder(); + void detectGtkFrameExtents(); void destroyDecoration(); void updateFrameExtents(); @@ -1016,6 +1024,7 @@ private: QPalette m_palette; QList m_connections; + bool m_clientSideDecorated; }; /** @@ -1049,6 +1058,11 @@ inline xcb_window_t Client::decorationId() const return XCB_WINDOW_NONE; } +inline bool Client::isClientSideDecorated() const +{ + return m_clientSideDecorated; +} + inline const Client* Client::transientFor() const { return transient_for; diff --git a/events.cpp b/events.cpp index 514eecc69d..7736e614e1 100644 --- a/events.cpp +++ b/events.cpp @@ -869,6 +869,8 @@ void Client::propertyNotifyEvent(xcb_property_notify_event_t *e) updateColorScheme(); else if (e->atom == atoms->kde_screen_edge_show) updateShowOnScreenEdge(); + else if (e->atom == atoms->gtk_frame_extents) + detectGtkFrameExtents(); break; } } diff --git a/manage.cpp b/manage.cpp index fbdf195704..892685f416 100644 --- a/manage.cpp +++ b/manage.cpp @@ -120,6 +120,7 @@ bool Client::manage(xcb_window_t w, bool isMapped) if (Xcb::Extensions::self()->isShapeAvailable()) xcb_shape_select_input(connection(), window(), true); detectShape(window()); + detectGtkFrameExtents(); detectNoBorder(); fetchIconicName(); getWMHints(); // Needs to be done before readTransient() because of reading the group diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index feeb288250..34dedb724a 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -2,8 +2,10 @@ install( DIRECTORY videowall DESTINATION ${DATA_INSTALL_DIR}/${KWIN_NAME}/scripts ) install( DIRECTORY synchronizeskipswitcher DESTINATION ${DATA_INSTALL_DIR}/${KWIN_NAME}/scripts ) install( DIRECTORY desktopchangeosd DESTINATION ${DATA_INSTALL_DIR}/${KWIN_NAME}/scripts ) +install( DIRECTORY enforcedeco DESTINATION ${DATA_INSTALL_DIR}/${KWIN_NAME}/scripts ) # service files install( FILES videowall/metadata.desktop DESTINATION ${SERVICES_INSTALL_DIR} RENAME kwin-script-videowall.desktop ) install( FILES synchronizeskipswitcher/metadata.desktop DESTINATION ${SERVICES_INSTALL_DIR} RENAME kwin-script-synchronizeskipswitcher.desktop ) install( FILES desktopchangeosd/metadata.desktop DESTINATION ${SERVICES_INSTALL_DIR} RENAME kwin-script-desktopchangeosd.desktop ) +install( FILES enforcedeco/metadata.desktop DESTINATION ${SERVICES_INSTALL_DIR} RENAME kwin-script-enforcedeco.desktop ) diff --git a/scripts/enforcedeco/contents/code/main.js b/scripts/enforcedeco/contents/code/main.js new file mode 100644 index 0000000000..fa257b8a7e --- /dev/null +++ b/scripts/enforcedeco/contents/code/main.js @@ -0,0 +1,38 @@ +/* + * Copyright 2014 Martin Gräßlin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ +function enforceDeco(client) { + if (client.noBorder && client.clientSideDecorated) { + client.noBorder = false + } +} + +function setupConnection(client) { + enforceDeco(client); + client.clientSideDecoratedChanged.connect(client, function () { + enforceDeco(this); + }); +} + +workspace.clientAdded.connect(setupConnection); +// connect all existing clients +var clients = workspace.clientList(); +for (var i=0; i