Tk Source Code

View Ticket
Login
Ticket UUID: 7c51624a96ab209203dffda28d27c9cfc7743797
Title: The mouse in the room (paned windows).
Type: Bug Version: 9.1
Submitter: marc_culler Created on: 2025-03-17 22:33:41
Subsystem: 21. [panedwindow] Assigned To: nobody
Priority: 5 Medium Severity: Important
Status: Open Last Modified: 2025-03-24 12:46:25
Resolution: None Closed By: nobody
    Closed on:
Description:
OK, it isn't an elephant.  But it has been around for a long time now and
no one has been willing to acknowledge its existence.  Perhaps that is an
indication of how many people are using paned windows.

The paned windows are completely broken.  To demonstrate, run the widget demo
"A text widget with embedded windows and other features."  Click the button
"split windows" which adds a second pane to the paned window containing the
text widget.  (The right pane contains a peer widget.)

The pane sash works as expected until the toplevel window is resized.  Once
that happens the right pane (which is a "-stretch always" pane) goes
completely crazy.  Moving the sash any amount can cause the right pane to
have size 0 or to have size 32483 or many sizes in between none of which
preserve the size of the toplevel. Resizing the window after the pane size
has gone crazy can cause the window to suddenly have width 0 and to appear
as a thin white line on the screen.

This behavior is common to all platforms.
User Comments: marc_culler (claiming to be Marc Culler) added on 2025-03-24 12:46:25:
I added the new X11 stub Tk_SetSizeHints as described earlier and
it is now being used where Tk_SetGrid had been used before in the
file tkTrayIcon.c.  (Its purpose is to prevent the window manager
from changing the size of the tray icon window, not to do anything
like what "gridding" a window is supposed to do.) So now the only
use of Tk_SetGrid is in the function WmGridCmd which implements
the wm grid command.  That will be replaced by a no-op command.
Possibly it should produce a deprecation message.

Kevin and I both tested the current implementation of the tk systray
command and it works fine on all of the five window managers that we
tested with.

The uses of -setgrid in the demos have been removed as well.

marc_culler (claiming to be Marc Culler) added on 2025-03-23 22:02:53:
I asked ChatGPT if it could find a single example of a Tk application which
uses the command "wm grid" or a tkinter application which uses the method
wm_grid.  After telling me incorrectly that the method wm_grid does not exist
in tkinter, it said:

   That said, finding a specific example of a larger, real-world application
   that explicitly uses wm grid is difficult because it is not a widely-adopted
   or popular method. Most Tcl/Tk applications and libraries do not use it in
   their codebase.

   However, there might be some niche applications or specific use cases
   where wm grid is used for custom window management or to interact with
   specific window managers that do support grid-like behavior for top-level
   windows. Unfortunately, there's no well-known, widely-used Tcl/Tk
   application that is known to employ wm grid.

   To answer your question directly: No, I was unable to find any widely
  -used or notable Tcl/Tk application that explicitly uses the wm grid
   command.

marc_culler (claiming to be Marc Culler) added on 2025-03-23 21:48:04:
I must retract my false claim about the documentation. One juat has to look in
the right place, namely the man page for Tk_SetGrid, not for the -setgrid
oprion or the wm grid command.  That man page says:

    For each toplevel window there can be at most one internal window
    with gridding enabled. If Tk_SetGrid or Tk_UnsetGrid is invoked when some
    other window is already controlling gridding for tkwin's toplevel, the
    calls for the new window have no effect.

Saying that the child has gridding "enabled" is not so clear however.  What that
meas is that the child is controlling the geometry of its toplevel, something
which is counterintuitive at best and which, as the split windows demo shows,
does not work.

marc_culler (claiming to be Marc Culler) added on 2025-03-23 21:05:17:
I think that a good way to handle allowing Tk and extensions to set the
XSizeHints values stored in the TkWmInfo struct would be to add a stub
Tk_SetXSizeHints which accepts arguments for the hints min_width,
max_width, min_height, and max_height.  Of course this would only be
useful for X11, but the corresponding fields in TkWmInfo exist for all
platforms even though only X11 uses them.

This does not allow setting all hints.  Specifically it is omitting the
hints width_inc and height_inc which are the ones that come into play if
a toplevel is "gridded" by one (or more?) of its children.  That is because
I am contending that "gridding" toplevels is not useful, and the point of
this exercise is to get rid of it.

If that stub existed then tkUnixSysTray could deal with the hints via the
usual mechanism using the UpdateGeometryInfo function.

marc_culler (claiming to be Marc Culler) added on 2025-03-23 19:07:27:
I finally figured out the other missing ingredient, and I can now explain why
TkSetGrid was (and is no longer) needed for tkUnixSysTray.c.  I realize that I
have said this before (sorry!) but with the tip of getrid_setgrid the tray icon
now *really* works.  I have tested carefully on the XOrg versions of GNOME,
GNOME Classic, mate, plasma, and xfce.

The extra ingredient is that I now set the XSizeHints whenever the trayicon
widget receives a ConfigureNotify event (including the ConfigureNotify for a
size of 1x1, which happens before the window is mapped).

Apparently X servers don't save the size hints.  It looks like they only
use the hints once.  So the hints need to be refreshed each time the window
size is changed to prepare for the next time.  And for the tray icon there will
be several attempts to make it be 1 pixel wide before the window manager stops
pestering us.

Looking at tkUnixWm.c you will see that the hints get updated whenever
UpdateGeometryInfo is called.  According to the comments in that file,
  UpdateGeometryInfo "is invoked when a top-level window is first mapped, and
  also as a when-idle function, to bring the geometry and/or position of
  a top-level window back into line with what has been requested by the
  user and/or widgets."
It gets called very frequently.  For example, in the widger demo it gets
called each time that the mouse pointer enters or leaves a hyperlink.

When UpdateGeometryInfo updates the size hints it uses values which have been
stored in the TkWmInfo struct associated with a toplevel.  That struct is
private to tkUnixWm.c, so those values can only be set by a funciton defined
in that file.  IMPORTANT NOTE: the TkWmInfo struct is private on Windows and
unix but on macOS is it public, as it is defined in tkMacOSXWm.h which can
be included in any file.

The TkSetGrid function sets the hint values in the TkWmInfo struct.  While this
should be a simple operation, it is not.  It is complicated for one reason.
That reason is the existence of the setgrid option.  Every hint has separate
cases for deciding what the value of the hint should be, depending on whether
the toplevel has been "gridded" by some random widget that it contains.  (And
I will repeat that no one says and no one knows what happens if there are
two widgets which "grid" their common containing toplevel by virtue of both
having the -setgrid option set to 1.)

Anyway, the reason that SetGrid was used in tkUnixSysTray.c had nothing to do
with ensuring that the widget could only be resized to multiples of w and h
(of course). The reason was that it was important to reset the hints every
time that the window manager changed the size of the tray icon, which it will
attempt several times during the life of the icon.  By calling SetGrid it
ensured that the hints would be reset when the icon was first mapped and
again each time that it was resized by the window manager, to prevent the
next attempt by the window manager to shrink it down to 1 pixel.

Storing the size hints in the tkWmInfo struct was a work around by the Tk
authors for the problem that X11 destroys the hints after each resize.  That
workaround would still be needed and would be left intact after TkSetGrid is
removed.

fvogel added on 2025-03-23 16:50:54:

Originally I was wondering why [cafa154041] (side question: usefulness of such uniformization??) didn't remove -setgrid altogether. I wondered if it was because setting -setgrid 1 was some equivalent sort of saying [wm grid ...]. I'm willing to believe you when you write this [wm grid] command is never needed, but I'm afraid we don't know if it is used (I mean: outside of Tk itself). It might not be needed, but it does not mean it does nothing since it sets the WM hints, and as you wrote X11 uses them, at least with some window managers, right ?


marc_culler (claiming to be Marc Culler) added on 2025-03-23 14:31:38:
It is true that the TkSetGrid code in the windows port fills in many
fields of the TkWmInfo struct which are intended to be used for setting
XHints.  In fact, the code of TkSetGrid is identical for all three
platforms.  That is because the files tkUnixWm.c, tkWinWm.c and tkMacOSXWm.c
all began life as verbatim copies of a file named tkWm.c, which is
mentioned in the comments but no longer exists.

Obviously those fields in the TkWmInfo are not used in Windows since Windows
does not use XHints.  Similarly for macOS.  So a lot of extra busywork is
being done by TkSetGrid on Windows and macOS for no purpose.

It does have a purpose on X11 systems.  But TkSetGrid is called in exactly
one place, in tkUnixSysTray.  (There is one other place, of course, namely
in WmGridCmd whose only purpose is to expose the TkSetGrid function for
use in Tcl code.  And as far as I know that command is never needed.)

marc_culler (claiming to be Marc Culler) added on 2025-03-23 14:06:54:
Hi François.  My assumption is that using -setgrid 1 is never needed for
anything on any platform.  Only text and listbox widgets have that option
and it does not work for text widgets as demonstrated by the demo mentioned
in this ticket.  Do you know of any situation with any platform where
something breaks if the option -setgrid 1 is replaced by -setgrid 0?

fvogel added on 2025-03-23 08:24:11:
Hmmm... Marc, don't you think your findings (the need to set the XSizeHints) is also what windows using "-setgrid true" may need (I mean, not specifically the systray icon). Which could mean we perhaps can't blindly remove the -setgrid option from text/listbox and remove [wm grid] because this would remove the size hints given to the window manager, on which code using these "-setgrid on" / [wm grid] might rely?

kevin_walzer added on 2025-03-23 01:02:00:
Marc, the update is now partly working on my system. When I first run the system tray demo, the icon appears briefly then disappears. However, when I hit the "modify" button, the second icon displays correctly.

marc_culler (claiming to be Marc Culler) added on 2025-03-22 22:53:29:
I can't explain why things were working for me yesterday, but I think I have
now actually gotten to the bottom.  The magical ingredient in TkSetGrid was
that it was setting the XSizeHints for the tray icon window.  My theory is
that some window managers will try to make the tray icon windows as thin as
possible (1 pixel wide in the case of GNOME).  To ask them not to do that
you need to set the XSizeHints.

Setting the hints does not take much code and that is now being done directly
in tkUnixSysTray.c, removing the need for calling TkSetGrid.

Kevin, would you kindly test the tip of the getrid_setgrid branch on your
system?  I think you will find that it works now.

marc_culler (claiming to be Marc Culler) added on 2025-03-22 16:22:55:
Well, I haven't gotten to the bottom of this.  Running the same code that I
ran to create the screenshots last night (which is the identical binary for
each window manager since they are all on the same VM) produces different
results this morning than it did last night Some platforms still work, but
some do not.  In the case of GNOME (with a black tray) you can see the image
appear but it is only one pixel wide, so it appears as a thin white line
almost the height of the tray.

My experience with Tk suggests that this is a race condition.  Perhaps the
size change is happening before the window manager is ready to handle it.
Perhaps the complex code in the SetGrid machinery takes long enough to run
that it delays the size change enough for it to work (and it also does the
size change as an idle task, which probably helps.)

marc_culler (claiming to be Marc Culler) added on 2025-03-22 13:03:26:
It is possible that I should be using Tk_ResizeWindow instead of
XResizeWindow.  I will look into that.

kevin_walzer added on 2025-03-22 04:55:26:
Well, whatever issues I am having are probably  limited to my system. Let's proceed.

marc_culler (claiming to be Marc Culler) added on 2025-03-22 03:58:02:
For good measure I will add one from the Ubuntu 24.04 host system, running
Xwayland.

marc_culler (claiming to be Marc Culler) added on 2025-03-22 03:37:14:
I will upload screenshots from GNOME, mate, plasma and xcfe taken on my VM.

kevin_walzer added on 2025-03-22 02:54:06:
Doesn't seem to work for me on Debian + XFCE, but if you confirm it works elsewhere, I will support moving forward.

marc_culler (claiming to be Marc Culler) added on 2025-03-22 02:16:55:
Since we can't edit fossil comments and I always make typos I should add:

XREsizeWindow -> XResizeWindow
wm setgrid  -> wm grid

marc_culler added on 2025-03-22 02:07:44:
Thanks Kevin.

I figured it out.  The result is in the branch getrid_setgrid.

The short answer (never mind how long it took to find the answer) is that the call
to SetGrid in tkUnixSysTray.c can be replaced by a call to XREsizeWindow.  This is
a 2-line change which avoids the huge amount of complexity built into the SetGrid
machinery.

To test I set up a Debian 12 VM with many window managers installed.  I tested on:

  * GNOME (I had to install an extension to support tray icons)
  * xfce
  * mate (I had to add a notification area to the panel)
  * plasma

I would be grateful if you could test the branch on your system.

The ultimate plan for the getrid_setgrid branch would be to remove the wm setgrid
command, which would be done by eliminating WmGridCmd, as well as the -setgrid
options for the listbox and text widgets.  There would then be several (at least
5) fields in the WmInfo struct which could also be removed along with the TkSetGrid
stub itself.

Possibly we will be forced to leave the TkSetGrid stub in place as a no-op and
to leave WmGridCmd in place as a command which does nothing. I hope not.

kevin_walzer added on 2025-03-22 01:14:05:
Tried to remove the call to Tk_SetGrid and replace with UpdateGeometryInfo, but that seems to have no effect. The icon does not display. 

Looking in the Unix source tree, Tk_SetGrid is called only in one other place, in the WmGridCmd. We might want to consider removing *that* command via a TIP as it doesn't seem to be used anywhere. As the system tray icon seems to break without Tk_SetGrid, I am no longer in favor of removing the function - that seems to be the one place where it's necessary.

kevin_walzer added on 2025-03-21 11:49:01:
I don't have time to set up additional build environments for Linux, but I will try to work with your suggested revision as I have time.

marc_culler (claiming to be Marc Culler) added on 2025-03-21 02:45:59:
Did you also try it with Gnome or KDE?

I think we need to know what it is doing.  How can it matter whether the
window is w pixels by h pixels, or 1 grid unit x 1 grid unit where the first
grid unit is w pixels and thecother grid unit is h pixels?  That makes no
sense to me. If the tray window is never resized how can it matter whether
the resizing is limited to  multiples of w and h?  That just doesn't add up.
 
Given the bizarre behavior that we have seen it cause, I feel very
uncomfortable about leaving Tk_SetGrid in place if we are not able to come
up with even a vaguely plausible explanation for why it is needed.

I do have one thought, though.  Tk_SetGrid does call UpdateGeometryInfo.
That function does a lot of stuff (and gets called when I did not expect
it to, such as everytime you activate deactivate a hyprlink in the main
window of the demo script).  Maybe all that is needed is to set
wmPtr->width and wmPtr->height and then call UpdateGeometryInfo (as an
idle task).  Can you try that?

I will see if I can set up an XFCE VM.

kevin_walzer added on 2025-03-21 02:16:22:
I commented out the call to Tk_SetGrid on Debian running XFCE. The systray demo did NOT display the tray icon in the expected place. After restoring the call, the icon displayed correctly. So it appears that the tray icon depends on this call on X11. I wonder if we could just set up the same function under a different name for this individual use case?

marc_culler (claiming to be Marc Culler) added on 2025-03-20 19:28:39:
So I tried that experiment.  I used the example code from the man page which
creates a tray icon that has an image of a book.  I just pasted the code into
wish9.1.  I could see no difference when the call to SetGrid was commented out.

I am using Ubuntu 24.04 with gnome. The "tray" is the bar at the top of the
screen.  In both cases the book icon appeared in the bar and was removed when
I closed the root window and wish9.1 terminated.

I was not able to see any effect when I clicked button 1 or 3 on the icon.  But
that was true in both cases.  I assume that Gnome doesn't support that.

marc_culler (claiming to be Marc Culler) added on 2025-03-20 19:04:23:
Thanks Kevin.  I was able to download tktray 1.3.9 from the page you
suggested.

The one and only call to Tk_SetGrid in tktray.c (and unix/tkUnixSystray.c)
is:

Tk_SetGrid(icon->drawingWin,1,1,w,h);

That means to make the icon window be 1 grid unit wide and 1 grid unit
high where the horizontal grid unit is w pixels and the vertical grid unit
is h pixels.  The values of w and h are always the width and height of the
icon image. If Tk_SetGrid worked as advertised it would only allow you
to resize the icon window in multiples of w and h.  So you could make it
2w x 2h or 3w x 5h, but obviously you would not want to do that.

According to the documentation, the icon window is meant to have the same
interface as a toplevel, so it can be resized in principle.  But I see no
advantage to limiting the size to be a multiple of the image size.  (Actually,
I see no value in allowing it to be resized at all.)

Moreover, the documentation says:
   "Requested size for icon is set according to the image's width and
    height, but obeying (or disobeying) this request is left for the
    tray."
As far as I can tell, given that the requested size is always the same as
the image size, the call to Tk_SetGrid should not have any effect.

I will try deleting the call in the current unix/tkSysTray.c and see if
it has any discernable effect.  Perhaps, since you are the expert, you
would be willing to do the same experiment.

kevin_walzer added on 2025-03-20 17:54:58:
Tktray links at the wiki:  https://wiki.tcl-lang.org/page/tktray

kevin_walzer added on 2025-03-20 17:37:06:
Tktray’s code is the basis for the tk systray command on  X11. The call is used only once, in the TrayIconRequestSize call. I’m not sure what effect making that no-op would have.

marc_culler (claiming to be Marc Culler) added on 2025-03-20 17:12:04:
How did you get the source code for tktray?  All I could find were broken
links, e.g. tktray.googlecode.com.

jan.nijtmans added on 2025-03-20 16:59:00:

Tk_SetGrid() is used in the "tktray" extension (that's the only one I found). It could become a no-op function. Maybe "tktray" starts misbehaving then ....


marc_culler (claiming to be Marc Culler) added on 2025-03-20 16:44:45:
Yes, I think that removing -setgrid means that the wm grid command should
also go.  Not doing that would make the surgery more complicated and
less effective.  The manual says they have the same functionality.

Questions for Jan: 

1. Do we have to make them no-ops or can they just be removed from 9.1?
If they have to remain as no-ops, should there be a warning message?
If so, how does one implement such a warning?  Is there an example?

2. Is it legal to remove stubs, namely Tk_SetGrid and Tk_UnsetGrid, in
the 9.1 (i.e. trunk) branch right now?

jan.nijtmans added on 2025-03-20 16:34:53:

How about "wm grid"? It has the same effect as "-setgrid 1" on any widget. Should we make that a no-op too?


fvogel added on 2025-03-20 16:23:46:
+1

jan.nijtmans added on 2025-03-20 16:08:05:

I agree getting rid of it.


marc_culler (claiming to be Marc Culler) added on 2025-03-20 15:35:06:
Having wrestled with the code which supports the -setgrid option, for
over a day I now have a concrete suggestion for the next release of Tk:

We should get rid of setgrid.
-----------------------------

First, it doesn't work.

Second, it is very complicated.

Third, it only affects the listbox and the text widget.

Fourth, it is useless.

Here is an explanation of the fourth point.  The idea seems to be that you
might want a toplevel containing a Text widget to only be sized to integer
multiples of the character width and line height of the Text.  But, in fact,
there is no reason why you would ever want to do that.  The character width
is a *typical* character width.  Most characters have a different width
unless you are using a fixed width font (which may have been common in 1999
but is not common any more). And other things, such as scrollbars and sashes
that might be in the window have their own width which is surely not an
integer multiple of the character width.  Lines also have different heights,
at least when there are other things on the line such as an embedded window.

The widget demo has all of those things - scrollbars, sashes, embedded
windows - and it also provides an excellent illustration of the first point.
It totally doesn't work (unless you remove the -setgrid option).

It may be a convenience to be able to specify the size of a Text widget
in characters and lines (I am not convinced) but it is not a convenience
for a toplevel to have its size specified in characters and lines just
because one of the many widgets inside of it has hijacked its geometry
management by having the -setgrid option set.  Also, the manual does not
even attempt to say what happens if a toplevel has two such widgets in it
and they have different character widths and line heights.

So let's just get rid of setgrid.  It was never a good idea and it has
been failing for at least 20 years.

The backwards compatibility requirements, whatever they may be, would
surely be satisfied by making the -setgrid option be a no-op for the
two widget classes Text and Listbox.

Also "setgrid" is only one letter off from being an anagram of "get rid".

marc_culler (claiming to be Marc Culler) added on 2025-03-20 13:43:50:
Thanks for the attachment François.

fvogel added on 2025-03-20 06:42:27:

About binary non-readable files in our repository, see Fixing old binary attachments I wrote many years ago. I have re-attached the fixed file to [917631ffff], you should be able to read it correctly now.


marc_culler (claiming to be Marc Culler) added on 2025-03-19 23:04:43:
The function Tk_SetGrid is not implemented in any generic file, it is
implemented separately for each platform (even though there is no "p"
in its name.  However, all three implementations appear to be identical.

So add that to the list of mysteries.

I was curious about the patch from 2004 in [917631ffff] but its bits
seem to have rotted.  I was not able to open the file and the "file"
command could only say that it was "data".

fvogel added on 2025-03-19 22:04:29:

See also [3466588] and [917631ffff]. Old story indeed.


marc_culler (claiming to be Marc Culler) added on 2025-03-19 19:38:48:
Removing "-setgrid true" in the demo makes it work correctly.

The manual says:

"when the user interactively resizes the top-level window that contains
the widget, the dimensions of the window will be displayed to the user
in grid units and the window size will be constrained to integral numbers
of grid units"

It seems like one possibility is that the panedwindow is unable to handle
a pane with constrained size.  Another possibility (maybe?) is that the
panedwindow gets a size in grid units and assumes they are pixels.

The fact that the problem does not arise (no matter how the pane is moved)
until the window gets resized should provide a clue.

marc_culler (claiming to be Marc Culler) added on 2025-03-19 19:27:11:
Nice observation, François!

That should help track this down.

What I see is that moving the sash causes the window size to change (by a
lot).

Could the problem be that the Text widget reports its size in grid
units while the paned window is expecting pizel units?  Or something
like that?

fvogel added on 2025-03-19 19:00:17:
Bingo.

Add  -setgrid true  when creating the text widgets and you can reproduce. So this only happens in this -setgrid case, which may explain why it was not noticed before.

fvogel added on 2025-03-19 18:56:20:

Interestignly the following script does not allow to reproduce, what could be the difference with the demo?

package require Tk
panedwindow .pw -orient horizontal
for {set i 1} {$i <= 2} {incr i} {
    text .pw.t$i -width 15 -height 8
    .pw.t$i insert end "Text widget $i"
}
.pw add .pw.t1 .pw.t2 -stretch always
pack .pw -expand 1 -fill both


oehhar added on 2025-03-19 15:29:36:

Marc, don't get me wrong. We need a TIP for that. I just wanted to express, that it might be possible to remove a feature for Tk 9.1

Take care, Harald


marc_culler (claiming to be Marc Culler) added on 2025-03-19 14:22:51:
I don't think that is a good way of deciding to remove a feature.  The
decision should not be made just because no one wants to bother to figure
out what the bug is.

Small bugs can have dramatic consequences when they affect drawing to the
screen.

Patching the ttk paned window could easily be much, much more work than
fixing what may be a small bug.

oehhar added on 2025-03-19 07:29:02:

Marc, if there is something to remove for 9.1, we should at least try it. Perhaps just leaving the option existant and make it non functional ?

Harald


marc_culler (claiming to be Marc Culler) added on 2025-03-19 03:40:54:
I'm not sure whether the Ttk panedwindow can provide everything in the
API for the Tk panedwindow.  I suspect not.  For example the Tk panedwindow
has an option -opaqueresize which does not seem to exist for the Ttk
version (and which looks like a very old-fashioned thing that might have
been needed when graphics cards were orders of magnitude slower than
they are now).

It isn't clear to me what our backwards compatibility contract implies
about something like this, where we are supporting options which may not
have been used by anyone in the last twenty years.  We probably should
have asked that question before 9.0 was released, but it was certainly
not a priority back then.

kevin_walzer added on 2025-03-19 02:16:25:
Is it even worth fixing? Would it be possible to alias this widget's commands to the ttk::paned window?

marc_culler (claiming to be Marc Culler) added on 2025-03-18 18:48:50:
I don't know how long this has been broken, but I checked 8.6.13 and it
also has the broken behavior.  It is not quite as extreme.  If you
activate the split windows, resize the toplevel a small amount, and then
move the sash to the left then the window gets resized to be about 100
pixels wide and the right pane disappears.

Attachments: