Xlib How Does This (Removing Window Decoration) Work? - c++

How does the following code remove window borders?
//note the struct is declared elsewhere, is here just for clarity.
//code is from [http://tonyobryan.com/index.php?article=9][1]
typedef struct Hints
{
unsigned long flags;
unsigned long functions;
unsigned long decorations;
long inputMode;
unsigned long status;
} Hints;
//code to remove decoration
Hints hints;
Atom property;
hints.flags = 2;
hints.decorations = 0;
property = XInternAtom(display, "_MOTIF_WM_HINTS", true);
XChangeProperty(display,window,property,property,32,PropModeReplace,(unsigned char *)&hints,5);
XMapWindow(display, window);
So far I have gathered that an Atom is a sort of identifier similar to Window and Display but I can't figure out where the Hints structure or the "_MOTIF_WM_HINTS" came from. Can anyone explain all of this code for me? Thanks in advance, ell.

It's hard to come by any sort of "official"-looking standard or such, but the
_MOTIF_WM_HINTS property does seem to come from the... Motif toolkit (ok, you
guessed that :-) ).
See the MotifZone site.
Warning: what follows is incomplete, but should shed some light I hope.
Documentation for the XmNmwmDecorations, XmNmwmFunctions and XmNmwmInputMode
functions of the toolkit indicates that that property is a bitmask of various
values used to control the appearance, functions (resize, move, ...) and input
mode that the window manager should provide/give to the window.
See man vendorshell, or Oreilly Motif reference books, Vol6a chapter 16.
Properties are a part of the whole X11 thing. A window can have any number of
properties defined on it. Properties have a name, but setting/getting
properties is done through an "atom" (identifier of sorts) to avoid sending
the whole string on the wire for every get/set call.
See Properties and Atoms
The currently window manager (if any) can react to window property
changes by setting the appropriate event filter and acting on PropertyNotify
events, or simply inspecting the properties the window has when it gets mapped (or moved, or whatever). If the window manager knows of the _MOTIF_WM_HINT property, it'll
interpret those and (hopefully) do what you want.
(Note: I'm not entierly sure if that privilege is devolved to the window
manager, or if other windows can listen to those "PropertyNotify" events. Not
sure that's actually relevant to your question.)
So the code you have works just fine as long as your window manager knows
about the _MOTIF_WM_HINTS property.
You start by getting the atom
(identifier/shortcut) for it with XInternAtom, and setting its value via XChangeProperty()
before the window is actually drawn via MapWindow() (not sure if that would
work if you do it after the MapWindow(), that could depend on your window
manager).
[Edit: setting the .decorations part to zero clears all the decoration bits, so this requests that the window manager leave your borders the hell alone, essentially.]
I can't come up with somewhere "official" with the definition of that struct. It's defined in lib/X11/MwmUtils.h of the openmotif-2.3.3 distribution. Not sure how the .flags entry is used (can't find the code for the life of me :-/ ) but I suspect it's used to indicate which of the {decoration, function, inputMode} "sub-properties" you're acting on. (Don't take my word for that.)
As a side note, using _MOTIF_WM_HINTS might not be your best option right
now. Have you looked at the Extended Window Manager hints
specification and other information/standards/drafts over at freedesktop? I'll wager most "modern" window managers/desktop environments/kitchen sinks will tend to adhere to that rather than keeping backwards compatibility with Motif. All depends on what you're coding for I guess.
Thanks for reading :-)

Have a look at the _MOTIF_WM_HINTS property. I changed your code to a working state:
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAPIT
#undef REPEAT
void waitxevt(Display* d, int type)
{
XEvent e; /* XEvent holder */
do { XNextEvent(d, &e); } while (e.type != type);
}
void frame(Display* d, Window w, int e)
{
Atom window_type;
Atom motif_hints;
long value;
#ifdef MAPIT
XUnmapWindow(d, w);
waitxevt(d, UnmapNotify);
#endif
window_type = XInternAtom(d, "_NET_WM_WINDOW_TYPE", False);
if (e) value = XInternAtom(d, "_NET_WM_WINDOW_TYPE_NORMAL", False);
else value = XInternAtom(d, "_NET_WM_WINDOW_TYPE_DOCK", False);
XChangeProperty(d, w, window_type, XA_ATOM, 32, PropModeReplace, (unsigned char *) &value, 1);
long hints[5] = {e ? 0 : 2, 0, 0, 0, 0};
motif_hints = XInternAtom(d, "_MOTIF_WM_HINTS", False);
XChangeProperty(d, w, motif_hints, motif_hints, 32, PropModeReplace, (unsigned char *)&hints, 5);
#ifdef MAPIT
XMapWindow(d, w);
waitxevt(d, MapNotify);
#endif
}
int main(void) {
Display* d;
Window w;
XEvent e;
const char* msg = "Hello, World!";
int s;
GC gracxt;
int frmenb = 0;
d = XOpenDisplay(NULL);
if (d == NULL) {
fprintf(stderr, "Cannot open display\n");
exit(1);
}
s = DefaultScreen(d);
gracxt = XDefaultGC(d, s);
w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 640, 480, 5,
BlackPixel(d, s), WhitePixel(d, s));
XSelectInput(d, w, ExposureMask|KeyPressMask|StructureNotifyMask);
XMapWindow(d, w);
waitxevt(d, MapNotify);
while (1) {
XNextEvent(d, &e);
if (e.type == Expose) XDrawString(d, w, gracxt, 10, 50, msg, strlen(msg));
if (e.type == KeyPress) {
frame(d, w, frmenb);
frmenb = frmenb == 0 ? 1 : 0;
#ifdef REPEAT
frame(d, w, frmenb);
frmenb = !frmenb;
#endif
}
}
XCloseDisplay(d);
return 0;
}
The only thing changed is to include the hints.

Related

Why isn't SDL_CreateWindow showing a window when called from another class?

Rewriting this to try to provide some clarity and update the code with some things that have changed.
I am restructuring a project that used SDL2, and have encountered issues trying to create a blank window. I have attempted to structure the project similarly to the original by separating all functionality dealing with SDL_Window into its own class. If I move the call to SDL_CreateWindow into the same class as the event loop or move the event loop to the same class as the window, the window is created and shown as expected, however as it is now, the window appears to be created successfully (SDL_CreateWindow is not returning NULL) and the program doesn't seem to be hanging, but it does not display a window while the program is running.
The SDL_Window is created in the Graphics class and stored in a member variable:
Graphics::Graphics(const char* title, unsigned int w, unsigned int h, unsigned int flags, int& status) {
screen = SDL_CreateWindow(title,
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
w, h,
flags);
status = 0;
if (screen == NULL)
status = 1;
}
Graphics is instantiated in the Window class and stored in a member variable.
Window::Window(const char* title, unsigned int w, unsigned int h, unsigned int flags, int& status) {
g = Graphics(title, w,h, flags, status);
}
Window is instantiated in main, and if the window is created successfully, it starts the event loop.
{
int status;
Window window("Mirari", 640,480, SDL_WINDOW_SHOWN, status);
if (status == 0) {
window.eventLoop();
} else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window and renderer: %s", SDL_GetError());
return 1;
}
}
The event loop itself to be thorough (update and draw are both currently empty functions).
void Window::eventLoop() {
SDL_Event ev;
bool running = true;
while (running) {
const int start_time = SDL_GetTicks();
while (SDL_PollEvent(&ev)) {
switch (ev.type) {
case SDL_QUIT:
running = false;
break;
default:
break;
}
}
//update();
//draw();
std::cout << "." << std::endl;
const int elapsed = SDL_GetTicks() - start_time;
if (elapsed < 1000 / FPS)
SDL_Delay(1000 / FPS - elapsed);
}
}
SDL is initialized with this static function and these flags.
void Window::init(unsigned int sdl_flags, IMG_InitFlags img_flags) {
SDL_Init(sdl_flags);
IMG_Init(img_flags);
TTF_Init();
}
...
Window::init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER, IMG_INIT_PNG);
I know that a window can be created in a separate class because the first version of this project did that and it worked, I'm just not sure what has changed that is causing the window not to show up.
As said by some programmer dude, you design is not perfect and should be thought again.
Nevertheless, from what we can see on your code : If the Window constructor is called (and the SDL_Init was called before, which I assume so), then the windows should be created.
From there we only can guess what we can't see (as it's not part of what you are displaying) :
is the definition of SDL_WINDOWPOS_UNDEFINED, the same in both context ?
is the screen variable definition the same in both context ?
is the "screen" used in "update", or "draw" method, and, as uninitialized : it fails
... ?
As you probably are new to development, I suggest you adopt this habit very early : your code should check and report everything it does. A good program is easy to debug, as it says what's wrong
For instance, just after :
screen = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
w, h, flags);
you may want to write something like :
if(!screen)
{
std::cout << "Failed to create window\n";
return -1;
}
or better :
if(!screen)
{
throw std::exception("Failed to create window\n");
}
And so on.
For instance, in your function update, you may want to have something like :
if(!screen)
{
throw std::exception("Unable to update the display as it is uninitialized\n");
}
I assume your application would not end without any comment... but that's a guess

Qt - Is there a way to check if the window is marked always-on-top (linux)?

Is there a way to check if the Qt window is marked always-on-top (by the user)? I would like to check that on closeEvent() and save it for the next time the user opens the window.
P.S: I checked the windowFlags hoping that Qt.WindowStaysOnTopHint flag will be set, but the flags don't seem to be affected.
Using xlib, the needed window state hint can be checked calling the XGetWindowProperty function.
Check requisites first, e.g. sudo apt-get install libx11-dev.
In the pro file, link xlib and require the x11extras qt module.
QT += x11extras
LIBS += -lX11
This is a working example, a function that returns true if the passed-in widget pointer points to a always-on-top window:
#include <X11/Xlib.h>
#include <QtX11Extras/QX11Info>
bool isAlwaysOnTop(QWidget * widget)
{
Atom atr;
int afr;
unsigned long items;
unsigned long bytes;
unsigned char *data;
Display * display = QX11Info::display();
Atom property = XInternAtom(display, "_NET_WM_STATE", False);
if(XGetWindowProperty(display, widget->winId(), property, 0L, 1L, False, 4, &atr, &afr, &items, &bytes, &data) == 0)
{
Atom abv = XInternAtom(display, "_NET_WM_STATE_ABOVE", False);
Atom res = reinterpret_cast<Atom *>(data)[0];
return (res==abv);
}
return false;
}
It can be used from inside the widget closeEvent:
void Form::closeEvent(QCloseEvent *)
{
qDebug() << isAlwaysOnTop(this);
}

Xdamage: get event on window content changes

I am a beginner in xlib and I want to catch event on window content changes. For examples if some region changed. I've read that I need to use Xdamage for that and this is my code
Display* display = XOpenDisplay(NULL);
Window root = DefaultRootWindow(display);
XWindowAttributes attributes = {0};
XGetWindowAttributes(display, root, &attributes);
int width, height;
width = attributes.width;
height = attributes.height;
//XDAMAGE INIT
int damage_event, damage_error, test;
test = XDamageQueryExtension(display, &damage_event, &damage_error);
Damage damage = XDamageCreate(display, root, XDamageReportNonEmpty);
while (true){
XEvent event;
XNextEvent(display,&event);
//HERE I GET EVENT
XDamageSubtract(display,damage,None,None);
}
XCloseDisplay(display);
So, I have a working example in which I can get event and its type.The types, as I understand are xlib types - https://tronche.com/gui/x/xlib/events/types.html. But how can I find out from event that window was changed.
What you are actually receiving are DamageNotify events in your normal event loop that look like so:
typedef struct {
int type; /* event base */
unsigned long serial;
Bool send_event;
Display *display;
Drawable drawable;
Damage damage;
int level;
Bool more; /* more events will be delivered immediately */
Time timestamp;
XRectangle area;
XRectangle geometry;
} XDamageNotifyEvent;
according to the documentation from https://www.x.org/releases/current/doc/damageproto/damageproto.txt
The area member reports the damaged rectangular area as an XRectangle.
The drawable member normally reports the window that was damaged.
Note XNextEvent will always return an XEvent That is the static type returned by the function. It also has a dynamic type that depends on the ev->type member. - Check its type field to find out if you may cast it to an XDamageEvent:
XEvent ev;
/* We might need a pointer to an XDamageEvent... */
XDamageNotifyEvent *dev;
while (1) {
XNextEvent(display,&ev);
switch (ev.type){
...
case DamageNotify:
dev = (XDamageNotifyEvent*)&ev; /* if type matches, cast to XDamageNotify event */
printf ("Damage notification in window %d\n", dev->drawable);
printf ("Rectangle x=%d, y=%d, w=%d, h=%d\n",
dev->area.x, dev->area.y,
dev->area.width, dev->area.height);
break;
...
}
Late, but for anyone looking for the area that actually changed (ev->area are the window coordinates and size, for me at least):
auto ev = (XDamageNotifyEvent*)e;
auto region = XFixesCreateRegion(display, null, 0);
XDamageSubtract(display, ev->damage, None, region);
int count;
auto area = XFixesFetchRegion(display, region, &count);
if(area){
for(int i; i < count; i++){
auto rect = area[i];
// do something with rect
}
XFree(area);
}
XFixesDestroyRegion(display, region);

(C++) MessageBox for Linux like in MS Windows

I need to implement a simple graphical message box for a Linux (SDL) application similar to the Windows MessageBox in C++ (gcc/g++ 4.4.0). All it needs to do is to display a caption, a message and an ok or close button and to return to the calling function when that button is clicked.
SDL just uses X(11) to open a window for (OpenGL) rendering.
I have looked through a similar thread regarding a GTK implementation, but that implementation doesn't seem to work properly.
I have also tried wxWidgets' wxMessageBox function, but compiling the headers makes the compiler throw error messages about syntax errors in include/c++/4.4.0/bits/stl_algobase.h (gcc 4.4.0 32 bits on openSuSE 11.1 32 bits). Using wxWidgets also means having to link a ton of libraries, adding STL to my app (Which it doesn't need otherwise) and who knows what else, so I do not want to use wxWidgets.
X11/motif (openmotif) has what I need (XmCreate{Error|Warning|InfoDialog), but these need a parent widget (e.g. top level window) which I don't have and do not accept a NULL parameter for these.
So I am stumped right now. Is there a simple way to do what I want? Or at least a halfway simple/easy/straightforward one? If yes, which one (giving as many details as possible would be highly appreciated).
In SDL2, you can now show message boxes:
http://wiki.libsdl.org/SDL_ShowSimpleMessageBox
int SDL_ShowSimpleMessageBox(Uint32 flags,
const char* title,
const char* message,
SDL_Window* window)
http://wiki.libsdl.org/SDL_ShowMessageBox
int SDL_ShowMessageBox(const SDL_MessageBoxData* messageboxdata,
int* buttonid)
I personally use Qt4's QMessageBox.
example:
QMessageBox mb(QMessageBox::Question, "Title", "Message", QMessageBox::Ok | QMessageBox::Cancel);
if(mb.exec() == QMessageBox::Ok) { do_stuff(); }
Looks like you will have to create a top-level X11/Motif window. Here's some code to get you started:
#include <Xm/Xm.h>
#include <Xm/PushB.h>
/* Prototype Callback function */
void pushed_fn(Widget , XtPointer ,
XmPushButtonCallbackStruct *);
main(int argc, char **argv)
{ Widget top_wid, button;
XtAppContext app;
top_wid = XtVaAppInitialize(&app, "Push", NULL, 0,
&argc, argv, NULL, NULL);
button = XmCreatePushButton(top_wid, "Push_me", NULL, 0);
/* tell Xt to manage button */
XtManageChild(button);
/* attach fn to widget */
XtAddCallback(button, XmNactivateCallback, pushed_fn, NULL);
XtRealizeWidget(top_wid); /* display widget hierarchy */
XtAppMainLoop(app); /* enter processing loop */
}
void pushed_fn(Widget w, XtPointer client_data,
XmPushButtonCallbackStruct *cbs)
{
printf("Don't Push Me!!\n");
}
This was copied from here which might give you some more pointers on this.
Here is my solution. I chose to use Motif (OpenMotif) as it requires comparably few extra libraries (Xm, Xt, X11). Depending on the message size, my implementation opens a simple message box or a more sophisticated dialog with a non editable, scrollable text (the latter taken from the Motif programmer's manual and adapted for my purposes).
Include files and global data:
#include <Xm/Xm.h>
#include <Xm/MwmUtil.h>
#include <Xm/MainW.h>
#include <Xm/CascadeB.h>
#include <Xm/MessageB.h>
#include <Xm/RowColumn.h>
#include <Xm/Form.h>
#include <Xm/PushBG.h>
#include <Xm/LabelG.h>
#include <Xm/PanedW.h>
#include <Xm/Text.h>
#include <Xm/DialogS.h>
#include <Xm/Command.h>
static XtAppContext appShell;
Helper function to determine rows and max. cols of a text message:
static int MsgSize (char* pszMsg, int& nCols)
{
if (!(pszMsg && *pszMsg))
return 0;
int nRows = 1;
nCols = 0;
for (char* p = pszMsg; *p && (pszMsg = strchr (p, '\n')); nRows++, p = ++pszMsg) {
if (nCols < pszMsg - p)
nCols = pszMsg - p;
}
return nRows;
}
Callback function for the message dialog's close button:
void DestroyShell (Widget widget, XtPointer clientData, XtPointer callData)
{
Widget shell = (Widget) clientData;
XtDestroyWidget (shell);
// tell the application event loop to terminate w/o terminating the application
XtAppSetExitFlag (appShell);
}
Build a dialog containing a scrollable, non editable text widget and a close button. Taken from the Motif programmer's manual and slightly adapted (no icon, single button), minimal window decoration).
void XmMessageDialog (const char* pszMsg, int nRows, int nCols, bool bError)
{
Widget msgBox, pane, msgText, form, widget;
void DestroyShell(Widget, XtPointer, XtPointer);
Arg args [10];
int n = 0;
int i;
Dimension h;
// Set up a DialogShell as a popup window. Set the delete window protocol response to XmDESTROY to make sure that
// the window goes away appropriately. Otherwise, it's XmUNMAP which means it'd be lost forever, since we're not storing
// the widget globally or statically to this function.
Widget topWid = XtVaAppInitialize (&appShell, "D2X-XL", NULL, 0, &argc, argv, NULL, NULL);
XtSetArg (args [0], XmNdeleteResponse, XmDESTROY);
msgBox = XmCreateDialogShell (topWid, bError ? const_cast<char*>("Error") : const_cast<char*>("Warning"), args, 1);
XtVaGetValues (msgBox, XmNmwmDecorations, &i, NULL);
i &= ~(MWM_DECOR_ALL | MWM_DECOR_MINIMIZE | MWM_DECOR_MAXIMIZE | MWM_DECOR_MENU);
XtVaSetValues (msgBox, XmNmwmDecorations, i, NULL);
XtVaGetValues (msgBox, XmNmwmFunctions, &i, NULL);
i &= ~(MWM_FUNC_ALL | MWM_FUNC_MINIMIZE | MWM_FUNC_MAXIMIZE | MWM_FUNC_CLOSE);
XtVaSetValues (msgBox, XmNmwmFunctions, i, NULL);
// Create a PanedWindow to manage the stuff in this dialog. PanedWindow won't let us set these to 0!
XtSetArg (args [0], XmNsashWidth, 1);
// Make small so user doesn't try to resize
XtSetArg (args [1], XmNsashHeight, 1);
pane = XmCreatePanedWindow (msgBox, const_cast<char*>("pane"), args, 2);
// Create a RowColumn in the form for Label and Text widgets. This is the control area.
form = XmCreateForm (pane, const_cast<char*>("form1"), NULL, 0);
// prepare the text for display in the ScrolledText object we are about to create.
n = 0;
XtSetArg (args [n], XmNscrollVertical, True); n++;
XtSetArg (args [n], XmNscrollHorizontal, False); n++;
XtSetArg (args [n], XmNeditMode, XmMULTI_LINE_EDIT); n++;
XtSetArg (args [n], XmNeditable, False); n++;
XtSetArg (args [n], XmNcursorPositionVisible, False); n++;
XtSetArg (args [n], XmNwordWrap, True); n++;
XtSetArg (args [n], XmNvalue, pszMsg); n++;
XtSetArg (args [n], XmNrows, min (nRows, 30)); n++;
XtSetArg (args [n], XmNcolumns, min (nCols, 120)); n++;
msgText = XmCreateScrolledText (form, const_cast<char*>("help_text"), args, n);
// Attachment values must be set on the Text widget's PARENT, the ScrolledWindow. This is the object that is positioned.
XtVaSetValues (XtParent (msgText),
XmNleftAttachment, XmATTACH_FORM,
XmNtopAttachment, XmATTACH_FORM,
XmNrightAttachment, XmATTACH_FORM,
XmNbottomAttachment, XmATTACH_FORM,
NULL);
XtManageChild (msgText);
XtManageChild (form);
// Create another form to act as the action area for the dialog
XtSetArg (args [0], XmNfractionBase, 5);
form = XmCreateForm (pane, const_cast<char*>("form2"), args, 1);
// The OK button is under the pane's separator and is attached to the left edge of the form. It spreads from
// position 0 to 1 along the bottom (the form is split into 5 separate grids via XmNfractionBase upon creation).
widget = XmCreatePushButtonGadget (form, const_cast<char*>("Close"), NULL, 0);
XtVaSetValues (widget,
XmNtopAttachment, XmATTACH_FORM,
XmNbottomAttachment, XmATTACH_FORM,
XmNleftAttachment, XmATTACH_POSITION,
XmNleftPosition, 2,
XmNrightAttachment, XmATTACH_POSITION,
XmNrightPosition, 3,
XmNshowAsDefault, True,
XmNdefaultButtonShadowThickness, 1,
NULL);
XtManageChild (widget);
XtAddCallback (widget, XmNactivateCallback, DestroyShell, (XtPointer) msgBox);
// Fix the action area pane to its current height -- never let it resize
XtManageChild (form);
XtVaGetValues (widget, XmNheight, &h, NULL);
XtVaSetValues (form, XmNpaneMaximum, h, XmNpaneMinimum, h, NULL);
// This also pops up the dialog, as it is the child of a DialogShell
XtManageChild (pane);
}
Callback function for the message box' Ok button
void XmCloseMsgBox (Widget w, XtPointer clientData, XtPointer callData)
{
XtAppSetExitFlag (appShell);
}
Decide whether to use the simple or advanced message box, display either of them, and remove them when the user clicks their close/ok button.
void XmMessageBox (const char* pszMsg, bool bError)
{
Widget topWid;
int nRows, nCols;
nRows = MsgSize (const_cast<char*>(pszMsg), nCols);
if ((nRows > 3) || (nCols > 360))
XmMessageDialog (pszMsg, nRows, nCols, bError);
else { // use the built-in message box
topWid = XtVaAppInitialize (&appShell, "D2X-XL", NULL, 0, &argC, argv, NULL, NULL);
// setup message box text
Arg args [1];
XmString xmString = XmStringCreateLocalized (const_cast<char*>(pszMsg));
XtSetArg (args [0], XmNmessageString, xmString);
// create and label message box
Widget xMsgBox = bError
? XmCreateErrorDialog (topWid, const_cast<char*>("Error"), args, 1)
: XmCreateWarningDialog (topWid, const_cast<char*>("Warning"), args, 1);
// remove text resource
XmStringFree (xmString);
// remove help and cancel buttons
XtUnmanageChild (XmMessageBoxGetChild (xMsgBox, XmDIALOG_CANCEL_BUTTON));
XtUnmanageChild (XmMessageBoxGetChild (xMsgBox, XmDIALOG_HELP_BUTTON));
// add callback to the "close" button that signals closing of the message box
XtAddCallback (xMsgBox, XmNokCallback, XmCloseMsgBox, NULL);
XtManageChild (xMsgBox);
XtRealizeWidget (topWid);
}
XtAppMainLoop (appShell);
XtUnrealizeWidget (topWid);
XtDestroyApplicationContext (appShell);
}
I would suggest that you look into one of the GUI libraries that support SDL as a backend. One such library would be GG, which has the class ThreeButtonDlg. When its Run() returns, you can look at its Result(). See the Initial method in their minimal example.

Handle "new top level window" events in Xlib/Xt

So I'm in a situation where I need to know when a top level window gets created. I'm working at the Xlib/Xt level and on a Window Manager that doesn't support the EWMH specification. My idea is to hook into the root window's SubstructureNotify events. But things are not as simple as just that.
The problem is that not every CreateNotify event corresponds to the creation of a [b]top level[/b] window. So what I think I need to do is test the window I get from the event somehow to confirm that it is a top level window. I've got close, but some spurious windows still make it through my net. For example, in a GTK application if you have a dropdown box and you click it, a new window is created that I can't figure out how to catch and ignore. Such a window is troublesomely indistinguishable from a typical top level application window.
Here's what I have so far:
// I am omiting (tons of) cleanup code and where I set the display and toplevel variables.
Display* display;
Widget toplevel;
bool has_name(Window window)
{
XTextProperty data = XTextProperty ();
return (!XGetWMName (display, window, &data));
}
bool has_client_leader(Window window)
{
unsigned long nitems = 0;
unsigned char* data = 0;
Atom actual_type;
int actual_format;
unsigned long bytes;
// WM_CLIENT_LEADER is an interned Atom for the WM_CLIENT_LEADER property
int status = XGetWindowProperty (display, window, WM_CLIENT_LEADER, 0L, (~0L), False,
AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes, &data);
if (status != Success || acutal_type == None) return false;
Window* leader = reinterpret_cast<Window*> (data);
return (*leader != 0);
}
bool has_class(Window window)
{
XClassHint data = XClassHint ();
return (!GetClassHint (display, window, &data));
}
void handle_event(Widget widget, XtPointer, XEvent* event, Boolean*)
{
if (event->type != CreateNotify) return;
Window w = event->xcreatewindow.window;
// confirm window has a name
if (!has_name (w)) return;
// confirm window is a client window
Window client = XmuClientWindow (display, w);
if (!client || client != w) return;
// confirm window has a client leader that is not 0x0
if (!has_client_leader (client)) return;
// confirm window has a class
if (!has_class (client)) return;
// The window has passed all our checks!
// Go on to do stuff with the window ...
}
int main(int argc, char* argv[])
{
// ...
// Setting up the event handler for SubstructureNotify on root window
Window root_window = XDefaultRootWindow (display);
Widget dummy = XtCreateWidget ("dummy", coreWidgetClass, toplevel, 0, 0);
XtRegisterDrawable (display, root_window, dummy);
XSelectInput (display, root_window, SubstructureNotifyMask);
XtAddRawEventHandler (dummy, SubstructureNotifyMask, False, handle_event, 0);
// ...
}
A long shot, but does anyone have any ideas I could try? I can't think of much else I can really do here.
I assume you're familiar with the ICCCM and its long-winded discussion.
Have you checked the WM_TRANSIENT_FOR property?