After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 576306 - gdkscreen.c: get_nearest_monitor too simple, gives wrong result, mispositioning tooltips
gdkscreen.c: get_nearest_monitor too simple, gives wrong result, mispositioni...
Status: RESOLVED FIXED
Product: gtk+
Classification: Platform
Component: Backend: X11
unspecified
Other All
: Normal normal
: ---
Assigned To: gtk-bugs
gtk-bugs
Depends on:
Blocks:
 
 
Reported: 2009-03-22 16:49 UTC by Dave Gilbert
Modified: 2009-03-24 05:59 UTC
See Also:
GNOME target: ---
GNOME version: 2.25/2.26


Attachments
fix for get_nearest_monitor (1.30 KB, patch)
2009-03-22 17:14 UTC, Dave Gilbert
none Details | Review

Description Dave Gilbert 2009-03-22 16:49:38 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.
Comment 1 Dave Gilbert 2009-03-22 17:14:10 UTC
Created attachment 131130 [details] [review]
fix for get_nearest_monitor

Adds a better distance measure to get_nearest_monitor.
Comment 2 Dave Gilbert 2009-03-22 23:14:25 UTC
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
Comment 3 Matthias Clasen 2009-03-22 23:50:34 UTC
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;
 	}
     }
Comment 4 Dave Gilbert 2009-03-23 19:13:09 UTC
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
Comment 5 Matthias Clasen 2009-03-24 05:59:09 UTC
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.