linuxmint / linuxmint/cinnamon
Inconsistent window positions
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 5.6k
- Forks
- 915
- Avg merge
- 5d 22h
- Merged PRs (30d)
- 3
Description
Distribution
Mint 21
Package version
5.4.12
Graphics hardware in use
GeForce RTX 2080
Frequency
Always
Bug description
First off, I am not writing a Cinnamon specific application. I'm only interacting with X11 APIs because my applications (games and other graphically intensive applications) provide their own UI for maximum portability.
That said, it's not easily possible to retrieve the position of my window, and reapply that position when the application starts. It's not entirely impossible, (I have finally figured it out - see below) but I suspect what I have is fairly hacky.
(I should point out, I not only need this for application startup. One use case for a video wall requires the application remember and reapply window positions when reconfigured on the fly to occupy between one and 10 displays.)
Steps to reproduce
Create a window with XCreateWindow. The returned window ID should be all I need to interact with that window.
Retrieve the position of that window with XGetWindowAttributes. The position is not given in desktop coordinates. It's the position of that window relative to its parent (that Cinnamon creates behind the scenes).
Apply the retrieved position with XMoveWindow. The window jumps to the top left corner of the desktop.
Expected behavior
Retrieving the window position and reapplying it should produce consistent results.
Additional information
In theory, the only thing I should need to interact with X11 windows on Cinnamon is the window ID returned from XCreateWindow.
In practice, this is not the case.
XCreateWindow returns a window ID. If I retrieve the position of that window with XGetWindowAttributes, the position returned is relative to the parent window that Cinnamon creates behind the scenes. However, the position returned from XGetWindowAttributes for that parent window is also not correct.
If I simply retrieve the position of the parent window and then apply that back to the parent window, the window jumps down ~28 pixels. The height of the title bar, I believe.
If I retrieve the position of the parent window and apply that position back to the child window (returned from XCreateWindow) the window jumps up and to the left by (as far as I can tell) the size of the window border and drop shadow.
If I try to save the position of the window when I receive ConfigureNotify events, the position is horribly inconsistent. When moving the window by dragging the title bar, the event data position is the parent window's position. When resizing the window by dragging its borders, the event data position is the child window's position.
I have worked around this problem, but I'm guessing what I have won't work for other window managers that do things properly.
My solution is as follows:
Ignore all position data given to me by ConfigureNotify events and always fetch the window position with the following code:
bool GetExtents(Display* display, Window windowID, glm::ivec4& extents) {
static Atom frameExtents = XInternAtom(display, "_NET_FRAME_EXTENTS", false);
Atom actualType;
int actualFormat;
unsigned long itemCount;
unsigned long bytesAfter;
unsigned char* data = nullptr;
if (!XGetWindowProperty(display, windowID, frameExtents, 0, 4, false, XA_CARDINAL, &actualType, &actualFormat, &itemCount, &bytesAfter, &data)) {
if (data) {
long* extentData = reinterpret_cast<long *>(data);
extents.x = static_cast<int>(extentData[0]);
extents.z = static_cast<int>(extentData[1]);
extents.y = static_cast<int>(extentData[2]);
extents.w = static_cast<int>(extentData[3]);
XFree(data);
return true;
}
}
return false;
}
glm::ivec2 getPosition(Display* display, Window windowID) const {
XWindowAttributes attr;
glm::ivec2 position;
glm::ivec4 extents;
Window root;
Window parent;
Window* children;
Window window;
uint32_t noofchildren;
XQueryTree(display, windowID, &root, &parent, &children, &noofchildren);
if (children) {
XFree(children);
}
if (root == parent) {
window = windowID;
}
window = parent ? parent : windowID;
XGetWindowAttributes(display, window, &attr);
position.x = attr.x;
position.y = attr.y;
// Hack ahead...
if (window != windowID) {
if (GetExtents(display, windowID, extents)) {
XGetWindowAttributes(display, windowID, &attr);
position.x += attr.x;
position.y += attr.y - extents.y;
}
}
return position;
}
By using that position along with the size retrieved with XGetWindowAttributes, I can reposition a window (the child window, to be exact) exactly where it was according to the user's input/application configuration. (With extra logic to handle maximised windows, of course.)
Essentially, this is now a null op:
Window windowID = XCreateWindow(...);
glm::ivec2 pos = getPosition(display, windowID);
XMoveWindow(display, windowID, pos.x, pos.y);
However, I shouldn't need to do any of that.
The position and size provided for ConfigureNotify events should be all I need to restore the position of the window returned by XCreateWindow.
Equally, the position and size returned by XGetWindowAttributes for the window returned from XCreateWindow should be all I need to later reposition my window(s).
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
No Cinnamon source file or test is identified. Start by reproducing the behavior with XCreateWindow, XGetWindowAttributes, ConfigureNotify, and XMoveWindow, then compare the reported coordinates with _NET_FRAME_EXTENTS; done means window positions and sizes can be saved and restored consistently without the workaround.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- linux
- Domain
- desktop, operating-systems
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100