diff --git a/client.cpp b/client.cpp index b0f1704f7d..c5246d9aec 100644 --- a/client.cpp +++ b/client.cpp @@ -130,6 +130,7 @@ Client::Client(Workspace* ws) , electricMaximizing(false) , activitiesDefined(false) , needsSessionInteract(false) + , input_window(None) { // TODO: Do all as initialization @@ -348,6 +349,52 @@ void Client::destroyClient() deleteClient(this, Allowed); } +void Client::updateInputWindow() +{ + QRegion region; + + if (!noBorder() && dynamic_cast(decoration)) { + // This function is implemented as a slot to avoid breaking binary + // compatibility + QMetaObject::invokeMethod(decoration, "region", Qt::DirectConnection, + Q_RETURN_ARG(QRegion, region), + Q_ARG(KDecorationDefines::Region, KDecorationDefines::ExtendedBorderRegion)); + } + + if (region.isEmpty()) { + if (input_window) { + XDestroyWindow(display(), input_window); + input_window = None; + } + return; + } + + QRect bounds = region.boundingRect(); + input_offset = bounds.topLeft(); + + // Move the bounding rect to screen coordinates + bounds.translate(geometry().topLeft()); + + // Move the region to input window coordinates + region.translate(-input_offset); + + if (!input_window) { + XSetWindowAttributes attr; + attr.event_mask = EnterWindowMask | LeaveWindowMask | + ButtonPressMask | ButtonReleaseMask | PointerMotionMask; + attr.override_redirect = True; + + input_window = XCreateWindow(display(), rootWindow(), bounds.x(), bounds.y(), + bounds.width(), bounds.height(), 0, 0, + InputOnly, 0, CWEventMask | CWOverrideRedirect, &attr); + } else { + XMoveResizeWindow(display(), input_window, bounds.x(), bounds.y(), + bounds.width(), bounds.height()); + } + + XShapeCombineRegion(display(), input_window, ShapeInput, 0, 0, region.handle(), ShapeSet); +} + void Client::updateDecoration(bool check_workspace_pos, bool force) { if (!force && @@ -382,6 +429,7 @@ void Client::updateDecoration(bool check_workspace_pos, bool force) destroyDecoration(); if (check_workspace_pos) checkWorkspacePosition(oldgeom); + updateInputWindow(); blockGeometryUpdates(false); if (!noBorder()) decoration->widget()->show(); @@ -415,6 +463,10 @@ void Client::destroyDecoration() emit geometryShapeChanged(this, oldgeom); } } + if (inputId()) { + XDestroyWindow(display(), input_window); + input_window = None; + } } bool Client::checkBorderSizes(bool also_resize) @@ -737,6 +789,7 @@ void Client::resizeDecoration(const QSize& s) } else { // oldSize != newSize resizeDecorationPixmaps(); } + updateInputWindow(); } bool Client::noBorder() const @@ -1185,8 +1238,11 @@ void Client::internalShow(allowed_t) mapping_state = Mapped; if (old == Unmapped || old == Withdrawn) map(Allowed); - if (old == Kept) + if (old == Kept) { + if (inputId()) + XMapWindow(display(), inputId()); updateHiddenPreview(); + } workspace()->checkUnredirect(); } @@ -1214,6 +1270,8 @@ void Client::internalKeep(allowed_t) mapping_state = Kept; if (old == Unmapped || old == Withdrawn) map(Allowed); + if (inputId()) + XUnmapWindow(display(), inputId()); updateHiddenPreview(); addWorkspaceRepaint(visibleRect()); workspace()->clientHidden(this); @@ -1238,6 +1296,8 @@ void Client::map(allowed_t) if (!isShade()) { XMapWindow(display(), wrapper); XMapWindow(display(), client); + if (inputId()) + XMapWindow(display(), inputId()); exportMappingState(NormalState); } else exportMappingState(IconicState); @@ -1258,6 +1318,8 @@ void Client::unmap(allowed_t) XUnmapWindow(display(), frameId()); XUnmapWindow(display(), wrapper); XUnmapWindow(display(), client); + if (inputId()) + XUnmapWindow(display(), inputId()); XSelectInput(display(), wrapper, ClientWinMask | SubstructureNotifyMask); if (decoration != NULL) decoration->widget()->hide(); // Not really necessary, but let it know the state @@ -2182,6 +2244,8 @@ void Client::updateCursor() if (decoration != NULL) decoration->widget()->setCursor(cursor); XDefineCursor(display(), frameId(), cursor.handle()); + if (inputId()) + XDefineCursor(display(), inputId(), cursor.handle()); if (moveResizeMode) // XDefineCursor doesn't change cursor if there's pointer grab active XChangeActivePointerGrab(display(), ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask, diff --git a/client.h b/client.h index d484f2f78c..c916029e38 100644 --- a/client.h +++ b/client.h @@ -90,6 +90,7 @@ public: Client(Workspace* ws); Window wrapperId() const; Window decorationId() const; + Window inputId() const { return input_window; } const Client* transientFor() const; Client* transientFor(); @@ -137,6 +138,7 @@ public: virtual QPoint clientPos() const; // Inside of geometry() virtual QSize clientSize() const; virtual QRect visibleRect() const; + QPoint inputPos() const { return input_offset; } // Inside of geometry() bool windowEvent(XEvent* e); virtual bool eventFilter(QObject* o, QEvent* e); @@ -571,6 +573,8 @@ private: void checkOffscreenPosition (QRect* geom, const QRect& screenArea); + void updateInputWindow(); + Window client; Window wrapper; KDecoration* decoration; @@ -726,6 +730,9 @@ private: bool activitiesDefined; //whether the x property was actually set bool needsSessionInteract; + + Window input_window; + QPoint input_offset; }; /** @@ -1022,6 +1029,7 @@ inline bool Client::hiddenPreview() const } KWIN_COMPARE_PREDICATE(WrapperIdMatchPredicate, Client, Window, cl->wrapperId() == value); +KWIN_COMPARE_PREDICATE(InputIdMatchPredicate, Client, Window, cl->inputId() == value); } // namespace diff --git a/events.cpp b/events.cpp index b18e2caa04..fd1147d22a 100644 --- a/events.cpp +++ b/events.cpp @@ -289,6 +289,9 @@ bool Workspace::workspaceEvent(XEvent * e) } else if (Client* c = findClient(FrameIdMatchPredicate(e->xany.window))) { if (c->windowEvent(e)) return true; + } else if (Client *c = findClient(InputIdMatchPredicate(e->xany.window))) { + if (c->windowEvent(e)) + return true; } else if (Unmanaged* c = findUnmanaged(WindowMatchPredicate(e->xany.window))) { if (c->windowEvent(e)) return true; @@ -1090,7 +1093,7 @@ bool Client::buttonPressEvent(Window w, int button, int state, int x, int y, int return true; } - if (w == wrapperId() || w == frameId() || w == decorationId()) { + if (w == wrapperId() || w == frameId() || w == decorationId() || w == inputId()) { // FRAME neco s tohohle by se melo zpracovat, nez to dostane dekorace updateUserTime(); workspace()->setWasUserInteraction(); @@ -1173,7 +1176,11 @@ bool Client::buttonPressEvent(Window w, int button, int state, int x, int y, int XAllowEvents(display(), ReplayPointer, CurrentTime); //xTime()); return true; } - if (w == decorationId()) { + if (w == decorationId() || w == inputId()) { + if (w == inputId()) { + x = x_root - geometry().x() + padding_left; + y = y_root - geometry().y() + padding_top; + } if (dynamic_cast(decoration)) // New API processes core events FIRST and only passes unused ones to the decoration return processDecorationButtonPress(button, state, x, y, x_root, y_root, true); @@ -1262,7 +1269,7 @@ bool Client::buttonReleaseEvent(Window w, int /*button*/, int state, int x, int XAllowEvents(display(), SyncPointer, CurrentTime); //xTime()); return true; } - if (w != frameId() && w != decorationId() && w != moveResizeGrabWindow()) + if (w != frameId() && w != decorationId() && w != inputId() && w != moveResizeGrabWindow()) return true; x = this->x(); // translate from grab window to local coords y = this->y(); @@ -1348,12 +1355,17 @@ void Client::checkQuickTilingMaximizationZones(int xroot, int yroot) // return value matters only when filtering events before decoration gets them bool Client::motionNotifyEvent(Window w, int /*state*/, int x, int y, int x_root, int y_root) { - if (w != frameId() && w != decorationId() && w != moveResizeGrabWindow()) + if (w != frameId() && w != decorationId() && w != inputId() && w != moveResizeGrabWindow()) return true; // care only about the whole frame if (!buttonDown) { QPoint mousePos(x, y); if (w == frameId()) mousePos += QPoint(padding_left, padding_top); + if (w == inputId()) { + int x = x_root - geometry().x() + padding_left; + int y = y_root - geometry().y() + padding_top; + mousePos = QPoint(x, y); + } Position newmode = mousePosition(mousePos); if (newmode != mode) { mode = newmode; diff --git a/geometry.cpp b/geometry.cpp index 528a0d852c..cd9d7a08f4 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -1917,8 +1917,13 @@ void Client::setGeometry(int x, int y, int w, int h, ForceGeometry_t force, bool XMoveResizeWindow(display(), window(), 0, 0, cs.width(), cs.height()); } updateShape(); - } else + } else { XMoveWindow(display(), frameId(), x, y); + if (inputId()) { + const QPoint pos = QPoint(x, y) + inputPos(); + XMoveWindow(display(), inputId(), pos.x(), pos.y()); + } + } // SELI TODO won't this be too expensive? sendSyntheticConfigureNotify(); updateWindowRules(); diff --git a/layers.cpp b/layers.cpp index 3ce9903808..efcfb872b9 100644 --- a/layers.cpp +++ b/layers.cpp @@ -163,10 +163,16 @@ void Workspace::propagateClients(bool propagate_new_clients) } #endif for (int i = stacking_order.size() - 1; i >= 0; i--) { - if (stacking_order.at(i)->hiddenPreview()) { + Client *client = stacking_order.at(i); + if (client->hiddenPreview()) { continue; } - newWindowStack << (Window*)stacking_order.at(i)->frameId(); + + if (client->inputId()) + // Stack the input window above the frame + newWindowStack << (Window*)client->inputId(); + + newWindowStack << (Window*)client->frameId(); } // TODO isn't it too inefficient to restack always all clients? // TODO don't restack not visible windows? diff --git a/manage.cpp b/manage.cpp index ebf3a54954..fe1086cf4c 100644 --- a/manage.cpp +++ b/manage.cpp @@ -149,6 +149,9 @@ bool Client::manage(Window w, bool isMapped) KStartupInfoData asn_data; bool asn_valid = workspace()->checkStartupNotification(window(), asn_id, asn_data); + // Make sure that the input window is created before we update the stacking order + updateInputWindow(); + workspace()->updateClientLayer(this); SessionInfo* session = workspace()->takeSessionInfo(this);