kwin: Insert an input-only window above each decorated client
This input-only window is used to capture events above the client window and preventing them from reaching the client. It is currently used to enlarge the borders by an invisible amount, using the ExtendedBorderRegion provided by the decoration.
This commit is contained in:
parent
3a91e4dee6
commit
9fa2feabc8
6 changed files with 106 additions and 8 deletions
66
client.cpp
66
client.cpp
|
@ -130,6 +130,7 @@ Client::Client(Workspace* ws)
|
||||||
, electricMaximizing(false)
|
, electricMaximizing(false)
|
||||||
, activitiesDefined(false)
|
, activitiesDefined(false)
|
||||||
, needsSessionInteract(false)
|
, needsSessionInteract(false)
|
||||||
|
, input_window(None)
|
||||||
{
|
{
|
||||||
// TODO: Do all as initialization
|
// TODO: Do all as initialization
|
||||||
|
|
||||||
|
@ -348,6 +349,52 @@ void Client::destroyClient()
|
||||||
deleteClient(this, Allowed);
|
deleteClient(this, Allowed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Client::updateInputWindow()
|
||||||
|
{
|
||||||
|
QRegion region;
|
||||||
|
|
||||||
|
if (!noBorder() && dynamic_cast<KDecorationUnstable*>(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)
|
void Client::updateDecoration(bool check_workspace_pos, bool force)
|
||||||
{
|
{
|
||||||
if (!force &&
|
if (!force &&
|
||||||
|
@ -382,6 +429,7 @@ void Client::updateDecoration(bool check_workspace_pos, bool force)
|
||||||
destroyDecoration();
|
destroyDecoration();
|
||||||
if (check_workspace_pos)
|
if (check_workspace_pos)
|
||||||
checkWorkspacePosition(oldgeom);
|
checkWorkspacePosition(oldgeom);
|
||||||
|
updateInputWindow();
|
||||||
blockGeometryUpdates(false);
|
blockGeometryUpdates(false);
|
||||||
if (!noBorder())
|
if (!noBorder())
|
||||||
decoration->widget()->show();
|
decoration->widget()->show();
|
||||||
|
@ -415,6 +463,10 @@ void Client::destroyDecoration()
|
||||||
emit geometryShapeChanged(this, oldgeom);
|
emit geometryShapeChanged(this, oldgeom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (inputId()) {
|
||||||
|
XDestroyWindow(display(), input_window);
|
||||||
|
input_window = None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::checkBorderSizes(bool also_resize)
|
bool Client::checkBorderSizes(bool also_resize)
|
||||||
|
@ -737,6 +789,7 @@ void Client::resizeDecoration(const QSize& s)
|
||||||
} else { // oldSize != newSize
|
} else { // oldSize != newSize
|
||||||
resizeDecorationPixmaps();
|
resizeDecorationPixmaps();
|
||||||
}
|
}
|
||||||
|
updateInputWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::noBorder() const
|
bool Client::noBorder() const
|
||||||
|
@ -1185,8 +1238,11 @@ void Client::internalShow(allowed_t)
|
||||||
mapping_state = Mapped;
|
mapping_state = Mapped;
|
||||||
if (old == Unmapped || old == Withdrawn)
|
if (old == Unmapped || old == Withdrawn)
|
||||||
map(Allowed);
|
map(Allowed);
|
||||||
if (old == Kept)
|
if (old == Kept) {
|
||||||
|
if (inputId())
|
||||||
|
XMapWindow(display(), inputId());
|
||||||
updateHiddenPreview();
|
updateHiddenPreview();
|
||||||
|
}
|
||||||
workspace()->checkUnredirect();
|
workspace()->checkUnredirect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1214,6 +1270,8 @@ void Client::internalKeep(allowed_t)
|
||||||
mapping_state = Kept;
|
mapping_state = Kept;
|
||||||
if (old == Unmapped || old == Withdrawn)
|
if (old == Unmapped || old == Withdrawn)
|
||||||
map(Allowed);
|
map(Allowed);
|
||||||
|
if (inputId())
|
||||||
|
XUnmapWindow(display(), inputId());
|
||||||
updateHiddenPreview();
|
updateHiddenPreview();
|
||||||
addWorkspaceRepaint(visibleRect());
|
addWorkspaceRepaint(visibleRect());
|
||||||
workspace()->clientHidden(this);
|
workspace()->clientHidden(this);
|
||||||
|
@ -1238,6 +1296,8 @@ void Client::map(allowed_t)
|
||||||
if (!isShade()) {
|
if (!isShade()) {
|
||||||
XMapWindow(display(), wrapper);
|
XMapWindow(display(), wrapper);
|
||||||
XMapWindow(display(), client);
|
XMapWindow(display(), client);
|
||||||
|
if (inputId())
|
||||||
|
XMapWindow(display(), inputId());
|
||||||
exportMappingState(NormalState);
|
exportMappingState(NormalState);
|
||||||
} else
|
} else
|
||||||
exportMappingState(IconicState);
|
exportMappingState(IconicState);
|
||||||
|
@ -1258,6 +1318,8 @@ void Client::unmap(allowed_t)
|
||||||
XUnmapWindow(display(), frameId());
|
XUnmapWindow(display(), frameId());
|
||||||
XUnmapWindow(display(), wrapper);
|
XUnmapWindow(display(), wrapper);
|
||||||
XUnmapWindow(display(), client);
|
XUnmapWindow(display(), client);
|
||||||
|
if (inputId())
|
||||||
|
XUnmapWindow(display(), inputId());
|
||||||
XSelectInput(display(), wrapper, ClientWinMask | SubstructureNotifyMask);
|
XSelectInput(display(), wrapper, ClientWinMask | SubstructureNotifyMask);
|
||||||
if (decoration != NULL)
|
if (decoration != NULL)
|
||||||
decoration->widget()->hide(); // Not really necessary, but let it know the state
|
decoration->widget()->hide(); // Not really necessary, but let it know the state
|
||||||
|
@ -2182,6 +2244,8 @@ void Client::updateCursor()
|
||||||
if (decoration != NULL)
|
if (decoration != NULL)
|
||||||
decoration->widget()->setCursor(cursor);
|
decoration->widget()->setCursor(cursor);
|
||||||
XDefineCursor(display(), frameId(), cursor.handle());
|
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
|
if (moveResizeMode) // XDefineCursor doesn't change cursor if there's pointer grab active
|
||||||
XChangeActivePointerGrab(display(),
|
XChangeActivePointerGrab(display(),
|
||||||
ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask,
|
ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask,
|
||||||
|
|
8
client.h
8
client.h
|
@ -90,6 +90,7 @@ public:
|
||||||
Client(Workspace* ws);
|
Client(Workspace* ws);
|
||||||
Window wrapperId() const;
|
Window wrapperId() const;
|
||||||
Window decorationId() const;
|
Window decorationId() const;
|
||||||
|
Window inputId() const { return input_window; }
|
||||||
|
|
||||||
const Client* transientFor() const;
|
const Client* transientFor() const;
|
||||||
Client* transientFor();
|
Client* transientFor();
|
||||||
|
@ -137,6 +138,7 @@ public:
|
||||||
virtual QPoint clientPos() const; // Inside of geometry()
|
virtual QPoint clientPos() const; // Inside of geometry()
|
||||||
virtual QSize clientSize() const;
|
virtual QSize clientSize() const;
|
||||||
virtual QRect visibleRect() const;
|
virtual QRect visibleRect() const;
|
||||||
|
QPoint inputPos() const { return input_offset; } // Inside of geometry()
|
||||||
|
|
||||||
bool windowEvent(XEvent* e);
|
bool windowEvent(XEvent* e);
|
||||||
virtual bool eventFilter(QObject* o, QEvent* e);
|
virtual bool eventFilter(QObject* o, QEvent* e);
|
||||||
|
@ -571,6 +573,8 @@ private:
|
||||||
|
|
||||||
void checkOffscreenPosition (QRect* geom, const QRect& screenArea);
|
void checkOffscreenPosition (QRect* geom, const QRect& screenArea);
|
||||||
|
|
||||||
|
void updateInputWindow();
|
||||||
|
|
||||||
Window client;
|
Window client;
|
||||||
Window wrapper;
|
Window wrapper;
|
||||||
KDecoration* decoration;
|
KDecoration* decoration;
|
||||||
|
@ -726,6 +730,9 @@ private:
|
||||||
bool activitiesDefined; //whether the x property was actually set
|
bool activitiesDefined; //whether the x property was actually set
|
||||||
|
|
||||||
bool needsSessionInteract;
|
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(WrapperIdMatchPredicate, Client, Window, cl->wrapperId() == value);
|
||||||
|
KWIN_COMPARE_PREDICATE(InputIdMatchPredicate, Client, Window, cl->inputId() == value);
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
20
events.cpp
20
events.cpp
|
@ -289,6 +289,9 @@ bool Workspace::workspaceEvent(XEvent * e)
|
||||||
} else if (Client* c = findClient(FrameIdMatchPredicate(e->xany.window))) {
|
} else if (Client* c = findClient(FrameIdMatchPredicate(e->xany.window))) {
|
||||||
if (c->windowEvent(e))
|
if (c->windowEvent(e))
|
||||||
return true;
|
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))) {
|
} else if (Unmanaged* c = findUnmanaged(WindowMatchPredicate(e->xany.window))) {
|
||||||
if (c->windowEvent(e))
|
if (c->windowEvent(e))
|
||||||
return true;
|
return true;
|
||||||
|
@ -1090,7 +1093,7 @@ bool Client::buttonPressEvent(Window w, int button, int state, int x, int y, int
|
||||||
return true;
|
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
|
// FRAME neco s tohohle by se melo zpracovat, nez to dostane dekorace
|
||||||
updateUserTime();
|
updateUserTime();
|
||||||
workspace()->setWasUserInteraction();
|
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());
|
XAllowEvents(display(), ReplayPointer, CurrentTime); //xTime());
|
||||||
return true;
|
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<KDecorationUnstable*>(decoration))
|
if (dynamic_cast<KDecorationUnstable*>(decoration))
|
||||||
// New API processes core events FIRST and only passes unused ones to the 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);
|
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());
|
XAllowEvents(display(), SyncPointer, CurrentTime); //xTime());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (w != frameId() && w != decorationId() && w != moveResizeGrabWindow())
|
if (w != frameId() && w != decorationId() && w != inputId() && w != moveResizeGrabWindow())
|
||||||
return true;
|
return true;
|
||||||
x = this->x(); // translate from grab window to local coords
|
x = this->x(); // translate from grab window to local coords
|
||||||
y = this->y();
|
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
|
// 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)
|
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
|
return true; // care only about the whole frame
|
||||||
if (!buttonDown) {
|
if (!buttonDown) {
|
||||||
QPoint mousePos(x, y);
|
QPoint mousePos(x, y);
|
||||||
if (w == frameId())
|
if (w == frameId())
|
||||||
mousePos += QPoint(padding_left, padding_top);
|
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);
|
Position newmode = mousePosition(mousePos);
|
||||||
if (newmode != mode) {
|
if (newmode != mode) {
|
||||||
mode = newmode;
|
mode = newmode;
|
||||||
|
|
|
@ -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());
|
XMoveResizeWindow(display(), window(), 0, 0, cs.width(), cs.height());
|
||||||
}
|
}
|
||||||
updateShape();
|
updateShape();
|
||||||
} else
|
} else {
|
||||||
XMoveWindow(display(), frameId(), x, y);
|
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?
|
// SELI TODO won't this be too expensive?
|
||||||
sendSyntheticConfigureNotify();
|
sendSyntheticConfigureNotify();
|
||||||
updateWindowRules();
|
updateWindowRules();
|
||||||
|
|
10
layers.cpp
10
layers.cpp
|
@ -163,10 +163,16 @@ void Workspace::propagateClients(bool propagate_new_clients)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
for (int i = stacking_order.size() - 1; i >= 0; i--) {
|
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;
|
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 isn't it too inefficient to restack always all clients?
|
||||||
// TODO don't restack not visible windows?
|
// TODO don't restack not visible windows?
|
||||||
|
|
|
@ -149,6 +149,9 @@ bool Client::manage(Window w, bool isMapped)
|
||||||
KStartupInfoData asn_data;
|
KStartupInfoData asn_data;
|
||||||
bool asn_valid = workspace()->checkStartupNotification(window(), asn_id, 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);
|
workspace()->updateClientLayer(this);
|
||||||
|
|
||||||
SessionInfo* session = workspace()->takeSessionInfo(this);
|
SessionInfo* session = workspace()->takeSessionInfo(this);
|
||||||
|
|
Loading…
Reference in a new issue