Bug 12871

Summary: Add user data field in X Window structure in Xlib
Product: xorg Reporter: Christian Casteyde <casteyde.christian>
Component: Lib/XlibAssignee: Xorg Project Team <xorg-team>
Status: CLOSED INVALID QA Contact: Xorg Project Team <xorg-team>
Severity: enhancement    
Priority: medium    
Version: unspecified   
Hardware: All   
OS: All   
Whiteboard:
i915 platform: i915 features:

Description Christian Casteyde 2007-10-20 10:54:00 UTC
There is no easy way to map X Window to language objects when retreiving a X event (or at least I didn't find one). So toolkits have to maintain a map with X Window ID as key and pointer to language object as value. This is cumbersome and not efficient. Last time I checked Qt main loop this was the way it works.

It would be usefull to have a way to attach user data to the Xlib Window/Widget struct, for instance of type "void *", and which toolkits could use to store language windows/widget object that manage it and retreive it easily. There would be both compatible (the field used to store it is ony in Xlib internal structure and doesn't have to be transfered to the server at first glance, but I don't know exactly how X protocol work so it may be not so trivial) and easy to use.

For instance, there is a "SetWindowLong" in MS Windows environment, that can be used to attach a data of type "long" to the window. This is not 64 bit efficient (hence my proposal for storing a "void *", and only one extra entry should be necessary then), and this is not used by Windows toolkits, but it could be the case (indeed, Unix based toolkits may not use it for port reasons, and MFC is completly broken in design anyway).

I don't know if X Window ID is maintained by the server (it would be logical for global namespace). If it was the case, and if Xlib couldn't have a way to associate easily client information to a window ID, it would be inefficient to call a "XWindowGetUserData" (because of network roundtrip to get the info from the server). In this case, this would be useless and local map lookup would be better. This would be sad.
This was just a proposition...
Comment 1 Daniel Stone 2007-10-20 16:12:46 UTC
Unfortunately, it'd be pretty much useless, since either the toolkit controlling the window used it, or nothing at all.  Otherwise, you could have fun collisions ...
Comment 2 Christian Casteyde 2007-10-21 01:30:51 UTC
Could you elaborate the "or nothing at all" part of your last comment (I didn't understand it)? Same for 'collisions' (with what, where?)?
OK, the toolkit knows the language object when it does a call to X to do something, but my proposal is for the main loop. For instance:

int CWindow::Map()
{
   return XMapWindow(m_display, m_winID);  // OK, we have m_winID and this pointer
}

but:

int CWindowedApp::RunMainLoop()
{
   // main event loop:
   ...
   XEvent evt;
   XNextEvent(m_display, &evt);
   // Now, we need to find the CWindow object to pass it the message:
   CWindow *pWindow = NULL;
   // First solution (cumbersome):
   WINDOW_MAP::iterator itWindow = m_mapKnownWindows.find(evt.window);
   pWindow = itWindow->second;

   // Second solution (more elegant and performant if this call remains
   // inside XLib and does not get to the server:
   CWindow *pWindow = NULL;
   XGetWindowData(evt.window, &pWindow);  // pWindow simply stored in XLib
             // structure for the Window ID

   // Now, pass the message:
   pWindow->OnMessage(&evt);

   // loop to the next message.
}

In the second example, the toolkit either has to maintain a window to language object map, for **all** known window, or it could use the proposed function to directly get the "user data object associated with the system object".

Generally speeking, many APIs enable passing "user data object pointer" to an operation or a system object in order to ease integration with object oriented language. I didn't found the equivalent in Xlib, it's sad and I don't think this would garbage the Xlib internals if it already maintains a data structure for each window ID (ie: if it is not a proxy to the server that simply encodes  the calls to X protocol). I admit not having looked in the Xlib source code (however I would directly have proposed a patch :-).

Note that this could have other usage than the main loop, because it is a common design pattern in APIs as mentionned above.
Comment 3 Daniel Stone 2007-10-21 02:24:55 UTC
Yes, I'm aware of the void *data pattern, but usually they have a very well-defined user.  What I'm saying is that if two things use it:
library A: XSetWindowData(window, some_pointer);
library B: XSetWindowData(window, other_pointer);
library A now does XGetWindowData, and it all goes horribly wrong.

So you have to add a pretty strict definition of what uses it, but it's pretty hard to sort it out: thanks to the magic of plugins, multiple toolkits and multiple languages in a single process, with a single window, is entirely possible (and does actually happen).  So it would seem that just keeping a list of windows and IDs is the safest for now.
Comment 4 Christian Casteyde 2007-10-21 03:49:44 UTC
Well, if multiple toolkits can handle the same window, it's quite a mess effectively. But this is only a problem because I made the assumption there was a n-1 relationship between windows and toolkits (ie: each window is handled through a single toolkit). That could be circumvented with something like that:

//Toolkit key:
XSetWindowData(DataKey, void *data);
XGetWindowData(DataKey, void **data);

and each toolkit can sets its own data pointer, provided they use unique DataKey (for instance an ATOM, or a const string specific to the toolkit, etc. I'm not going into details there).

So, given a toolkit, it could do:
XGetWindowData(m_MyToolkitKey, window, &pMyWindowObject);
with m_MyToolkitKey the key specific to the toolkit.

Thus, Xlib would only maintain "a few" user data fields, instead of all toolkits maintaining "a big" association between X windows and its objects (given that in a software, there are generally far more windows than toolkits used :-).

Note I do not adress which toolkit dispatches the message to the right object. Each toolkit can dispatch the messages for its window objects, but not for the other toolkits. However this is already a problem, and mixing different toolkits indeed requires toolkit cooperation and knowledge of each others in current applications, in order to expose hooks for other toolkits integration.

This leads me to another idea: X Window does not propose (same as first post, I didn't find a way to do that) a generic callback mechanism. To go further, something like that could be used:

int XSetWindowCallback(Window, here maybe event mask, int (*ToolkitCallback)(XEvent *, void *), void *user_data);

and ToolkitCallback would be called for each XEvent with user_data directly, and returns 0 if the event is not consumed (to call the ToolkitCallback for the next Toolkit). Please note that there is no more need of a specific toolkit key.

So, the creating toolkit registers its window event callback, with its user data object. If any other toolkit wants to get events, it could registers its own callback, and so on. When the event is dispatched, each toolkit could have a chance to get the event, with its objects directly available, until a toolkit "eats" the message completly.
Comment 5 Daniel Stone 2007-10-21 05:03:46 UTC
Heh, we're going down the road to reworking Xlib, where it's better off just left for dead, as it's deprecated by XCB.
Comment 6 Christian Casteyde 2007-10-21 05:51:08 UTC
OK, I'll look at XCB (in fact, I thought it was just internal changes but still with Xlib API).
Comment 7 Jamey Sharp 2007-10-21 12:47:13 UTC
> I don't think this would garbage the Xlib internals if it already
> maintains a data structure for each window ID (ie: if it is not a
> proxy to the server that simply encodes the calls to X protocol).

Xlib is exactly "a proxy to the server that simply encodes the calls to
X protocol", and so is XCB. (Xlib has a lot of other stuff too, but no
list of windows anywhere.) There's no place to put the hook you want in
either library, quite aside from the nightmare Daniel mentions of
designing an API for it.
Comment 8 Christian Casteyde 2007-10-21 13:42:42 UTC
OK, I've just looked at xcb_generate_id, where my hooks would have take place. It's quite obvious that these IDs are generated from the server and retreived by clients with prefetch for performance reasons.
So doing what I suggested would imply:
- either modify X protocol to transfert at least one user data field in each event (of course unacceptable);
- either make X lib/Xcb returns locally generated IDs referring to a table where the server IDs and user data could be found, and having the reverse association giving the local IDs from the server IDs on events. But this would place the job of finding local ID from server ID in Xlib instead of toolkits, and this association would have to be done for each event, whereas sometimes toolkit could ignore the event (for instance if they cache the object for the window that has the focus, they could pass kbd events directly). So this solution would be worse than the problem I'm trying to address;
- either doing both, with XLib/Xcb the only owner of an extra field of each events to find its local IDs, and maintaining the local IDs with user data and other goodies (certainly the best solution, but not feasible).
However I didn't considered designing an API an horrible task, this is simply not feasible technically in X to keep protocol compatibility, so I'm closing this bug.

Thanks for giving me clues about all this. This was a long standing idea that bothered me since quite a long...

Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.