diff --git a/internal/desktop/xorg.go b/internal/desktop/xorg.go index 270f7baf..1d1efef9 100644 --- a/internal/desktop/xorg.go +++ b/internal/desktop/xorg.go @@ -113,3 +113,7 @@ func (manager *DesktopManagerCtx) GetKeyboardModifiers() types.KeyboardModifiers func (manager *DesktopManagerCtx) GetCursorImage() *types.CursorImage { return xorg.GetCursorImage() } + +func (manager *DesktopManagerCtx) PutWindowBelow(window uint32) { + xorg.PutWindowBelow(window) +} diff --git a/internal/desktop/xorg/xorg.c b/internal/desktop/xorg/xorg.c index aded68c8..bb68a6ae 100644 --- a/internal/desktop/xorg/xorg.c +++ b/internal/desktop/xorg/xorg.c @@ -136,3 +136,46 @@ XFixesCursorImage *XGetCursorImage(void) { Display *display = getXDisplay(); return XFixesGetCursorImage(display); } + +void XPutWindowBelow(Window window) { + Display *display = getXDisplay(); + Window root = RootWindow(display, 0); + + // The WM_TRANSIENT_FOR property is defined by the [ICCCM] for managed windows. + // This specification extends the use of the property to override-redirect windows. + // If an override-redirect is a pop-up on behalf of another window, then the Client + // SHOULD set WM_TRANSIENT_FOR on the override-redirect to this other window. + // + // As an example, a Client should set WM_TRANSIENT_FOR on dropdown menus to the + // toplevel application window that contains the menubar. + + // Remove WM_TRANSIENT_FOR + Atom WM_TRANSIENT_FOR = XInternAtom(display, "WM_TRANSIENT_FOR", 0); + XDeleteProperty(display, window, WM_TRANSIENT_FOR); + + // Add _NET_WM_STATE_BELOW + XClientMessageEvent clientMessageEvent; + memset(&clientMessageEvent, 0, sizeof(clientMessageEvent)); + + // window = the respective client window + // message_type = _NET_WM_STATE + // format = 32 + // data.l[0] = the action, as listed below + // _NET_WM_STATE_REMOVE 0 // remove/unset property + // _NET_WM_STATE_ADD 1 // add/set property + // _NET_WM_STATE_TOGGLE 2 // toggle property + // data.l[1] = first property to alter + // data.l[2] = second property to alter + // data.l[3] = source indication + // other data.l[] elements = 0 + + clientMessageEvent.type = ClientMessage; + clientMessageEvent.window = window; + clientMessageEvent.message_type = XInternAtom(display, "_NET_WM_STATE", 0); + clientMessageEvent.format = 32; + clientMessageEvent.data.l[0] = 1; + clientMessageEvent.data.l[1] = XInternAtom(display, "_NET_WM_STATE_BELOW", 0); + clientMessageEvent.data.l[3] = 1; + + XSendEvent(display, root, 0, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&clientMessageEvent); +} diff --git a/internal/desktop/xorg/xorg.go b/internal/desktop/xorg/xorg.go index 22a745f6..de92246d 100644 --- a/internal/desktop/xorg/xorg.go +++ b/internal/desktop/xorg/xorg.go @@ -244,6 +244,13 @@ func GetCursorImage() *types.CursorImage { } } +func PutWindowBelow(window uint32) { + mu.Lock() + defer mu.Unlock() + + C.XPutWindowBelow(C.Window(window)) +} + //export goCreateScreenSize func goCreateScreenSize(index C.int, width C.int, height C.int, mwidth C.int, mheight C.int) { ScreenConfigurations[int(index)] = types.ScreenConfiguration{ diff --git a/internal/desktop/xorg/xorg.h b/internal/desktop/xorg/xorg.h index ef9c04f9..ea6fb385 100644 --- a/internal/desktop/xorg/xorg.h +++ b/internal/desktop/xorg/xorg.h @@ -28,3 +28,5 @@ short XGetScreenRate(); void XSetKeyboardModifier(int mod, int on); char XGetKeyboardModifiers(); XFixesCursorImage *XGetCursorImage(void); + +void XPutWindowBelow(Window window);