GNOME Bugzilla – Bug 576306
gdkscreen.c: get_nearest_monitor too simple, gives wrong result, mispositioning tooltips
Last modified: 2009-03-24 05:59:09 UTC
Please describe the problem: This problem is partially caused by gtk_tooltip_position but get_nearest_monitor is the killer. Consider two vertically stacked monitors, 0 at the top, 1 below it and an x,y position just below the bottom edge of the bottom monitor (1). If that value is passed to gdk_screen_get_monitor_at_point it will incorrectly return '0' as the monitor because get_nearest_monitor is too simple. get_nearest_monitor uses the MIN of the X and Y distance to the monitor to say which monitor is nearest but in this case the position is within the X range of both monitors and hence the first one to match is monitor 0. This bug is triggered by gtk_tooltip_position that positions the tooltip just below the cursor position (+cursorwidth/2, +cursorheight/2 from the current x/y) and hence if the cursor is at the bottom of monitor 1 the value passed to gdk_screen_get_monitor_at_point may be outside any monitors. Steps to reproduce: 1. Take two monitors with Xrandr stacked vertically, make the top one monitor '0' and the bottom monitor '1' 2. Position a button with a tooltip close to the bottom of monitor '1' (and hence the whole display) - gnome-calculator works OK as an example of this, also the icons on a panel at the bottom of monitor 1 also do it. 3. Hover the pointer right near the bottom (i.e. less than half cursor height above bottom edge of monitor one bottom). Actual results: The tooltip appears on monitor 0 - a whole monitor height above where it should Expected results: The tooltip should appear near the pointer, probably right at the bottom of the screen. Does this happen every time? Yes Other information: I don't think I ever saw this prior to 2.26; I'm on Ubuntu Jaunty alpha.
Created attachment 131130 [details] [review] fix for get_nearest_monitor Adds a better distance measure to get_nearest_monitor.
Actually thinking about that patch it should be possible to simplify it so the body of the code is something like: dist_total = (int)sqrt((double)(dist_x*dist_x + dist_y*dist_y)); but is sqrt((double)1)) guaranteed to round to an int as 1 or could the answer from approximation come out as 0.999something - if we have to check that then it would need after that: if ((dist_total==0) && ((dist_x!=0) || (dist_y!=0)) dist_total=1; which is a hell of a lot simpler than my first attempt at that patch. (The patch was tested, this simplification hasn't been). Dave
I'd just work with squares here, no need to take roots. No need for ifs either, all you need is to replace min(dist_x, dist_y) by dist_x * dist_x + dist_y * dist_y in the current code. You may want to store it in a temporary, of course. Like this: @@ -221,7 +221,7 @@ for (i = 0; i < num_monitors; i++) { GdkRectangle monitor; - gint dist_x, dist_y; + gint dist_x, dist_y, dist; gdk_screen_get_monitor_geometry (screen, i, &monitor); @@ -239,9 +239,10 @@ else dist_y = 0; - if (MIN (dist_x, dist_y) < nearest_dist) + dist = dist_x * dist_x + dist_y * dist_y; + if (dist < nearest_dist) { - nearest_dist = MIN (dist_x, dist_y); + nearest_dist = dist; nearest_monitor = i; } }
Ah yes of course. One worry I have is whether on a display larger than 32k accross we would overflow in that squaring. (I started wondering if actually the original code had intended to use MAX rather than MIN and that would be simpler). Dave
I decided to use the 1-norm instead. 2009-03-24 Matthias Clasen <mclasen@redhat.com> Bug 576306 – gdkscreen.c: get_nearest_monitor too simple, gives wrong result, mispositioning tooltips * gdk/gdkscreen.c (get_nearest_monitor): Make this function work. Problem reported by Dave Gilbert.