Open Bug 857126 Opened 12 years ago Updated 3 years ago

nsWindow::GetDPI returns bogus results on Windows

Categories

(Core :: Widget: Win32, defect)

x86_64
Windows 7
defect

Tracking

()

People

(Reporter: jfkthame, Assigned: jfkthame)

Details

Attachments

(1 file)

The widget API GetDPI(), which according to nsIWidget.h should return "the physical DPI of the screen containing the window", is severely broken on Windows, and returns meaningless results. AFAICT, the value is not really used - changing the implementation so that it returns a significantly different result doesn't seem to make any visible difference to the browser - but it is exposed via nsIDOMWindowUtils.displayDPI[1], which means front-end code and add-ons might try to use it (even though it's unclear what it means). On my Lenovo W510 (for example), which has a physical DPI of approximately 142, nsWindow::GetDPI is returning 72.0. Not even close. (I guess it's a good thing we aren't relying on it for anything critical!) In general, I don't think it's possible for GetDPI to reliably do what it claims; what, for example, should it do in a mirrored-screen configuration where the window is appearing simultaneously on a laptop screen with 140 physical dpi -and- a 24" external monitor with 96 dpi? For comparison, on the Mac we generally get something closer to a "true" result; nsIDOMWindowUtils.displayDPI on my Retina MacBook reports 221 dpi, which is correct for a screen that has 1800 pixels vertically, and measures about 207mm. However, it's also worth noting that the result on the Mac is in fact a "virtual" dpi value that depends on the display settings; it is NOT tied to the physical dpi of the hardware. It only matches because I happen to be running my system at the "Best for Retina" display setting. If I select "More Space" in the Display preference panel, nsIDOMWindowUtils.displayDPI changes to 294 - although the hardware has obviously not changed. I think the most reasonable thing to do here is to accept that GetDPI does -not- reliably do what nsIWidget.h claims, on either OS X or Windows at least,[2] and redefine GetDPI (and by extension nsIDOMWindowUtils.displayDPI, whose definition does not specify exactly what kind of "dpi" it's talking about anyway) to explicitly mean "pixels per logical inch". As such, it will depend not only on the hardware but also on any display-scaling options such as the Mac's Display preferences or the Windows Display control panel scaling options (100%, 125%, 150%, etc). [1] https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIDOMWindowUtils just says "The DPI of the display". [2] AFAICT, it won't necessarily be the true physical device DPI on Android, either; the implementation relies on DisplayMetrics.densityDpi, which will be a "quantized DPI" value. That's more analogous to Windows "logical DPI" than physical.
As for -why- GetDPI is completely bogus on Windows: it's using GetDeviceCaps(dc, VERTSIZE) to query the physical size of the screen. MSDN[1] claims this should return "Height, in millimeters, of the physical screen." But it doesn't. There's a warning note much later on that page: <quote> GetDeviceCaps reports info that the display driver provides. If the display driver declines to report any info, GetDeviceCaps calculates the info based on fixed calculations. If the display driver reports invalid info, GetDeviceCaps returns the invalid info. Also, if the display driver declines to report info, GetDeviceCaps might calculate incorrect info because it assumes either fixed DPI (96 DPI) or a fixed size (depending on the info that the display driver did and didn’t provide). Unfortunately, a display driver that is implemented to the Windows Display Driver Model (WDDM) (introduced in Windows Vista) causes GDI to not get the info, so GetDeviceCaps must always calculate the info. </quote> Which boils down to roughly: "You'll get whatever the driver decides to give you; but on Vista or later, it'll probably be pure fiction." In the case of my Lenovo, it returns a dimension that is in fact the *diagonal* of the screen, not the vertical size. There's a big difference. This does not appear to be a rare problem; a bit of Googling reveals that other people have also found GetDeviceCaps to be misleading (at best).[2] If we really want GetDPI to return "physical truth" on Windows, we could try reimplementing it based on the display EDID record.[3] However, as discussed above, I don't think this is particularly what we need, nor would it always be accurate (or even meaningful), nor would it match what we're doing on other platforms. Instead, I think we should update the comment in nsIWidget.h to reflect the reality that GetDPI returns a logical DPI value, and fix the Windows implementation so that it actually does so. (-Anything- would be better than the 72dpi that it's returning right now on my fairly-high-dpi laptop!) [1] http://msdn.microsoft.com/en-gb/library/windows/desktop/dd144877(v=vs.85).aspx [2] http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/6f06fa5e-1626-4668-b0ee-1f0d07e8d175/ [3] http://thetweaker.wordpress.com/2011/11/13/reading-monitor-physical-dimensions-or-getting-the-edid-the-right-way/
We can do searches across the add-on database to see if it's used there. (Not sure who to cc in for that though.) We should probably just depreciate the api and create a new entry point if we need one since it sounds like "GetDPI" isn't an accurate call name.
Assignee: nobody → jfkthame
Tryserver run in progress, to check this doesn't break unit tests: https://tbpl.mozilla.org/?tree=Try&rev=0a89ccfc733a Deprecating the API would be fine with me, though I don't think GetDPI is unreasonable - as long as it's documented that we mean logical DPI, not necessarily a physical measurement of the hardware. (I was going to make use of nsIDOMWindowUtils.displayDPI in a patch for bug 851520, which was what led me to discover that it's currently returning wildly inaccurate results. For that purpose, the system's "logical DPI" is exactly what I want - it reflects how the user intends the display to be treated - and it matches what Windows users simply call "dpi" when they're talking about their display settings: 96dpi, 120dpi, 144dpi, etc.)
Comment on attachment 732403 [details] [diff] [review] redefine nsIWidget::GetDPI as returning "logical" dpi, and fix the Windows implementation to match that. Review of attachment 732403 [details] [diff] [review]: ----------------------------------------------------------------- This DPI value is used to calculate the correct size of the mozmm unit (true physical mm). I don't think we should break or remove that; it has legitimate use-cases and I think it should be standardized in some form. If we can't get accurate information from Windows, that's too bad, but I think it's worth making a "best effort" to support this.
Attachment #732403 - Flags: review?(roc) → review-
In that case, both the Windows and OS X implementations are currently broken (and I think the Android one is too, although in practice it's probably fairly close on most devices). If we're using a "mozmm" unit that's based on this, then I presume that must be broken too, at least on Win7. (Where do we actually use it? Only on mobile?) Indeed... data:text/html,<div style="background:black;width:100mozmm;height:100mozmm"> gives me a 100mm square on OS X (correctly, for all resolution-scaling options, even though GetDPI does NOT return the true physical DPI as nsIWidget.h suggests it should, but rather the virtual DPI that the OS will simulate to us). OTOH, on Windows I get a much smaller square - about 67mm. (Which doesn't correspond in any obvious way to the almost 2:1 discrepancy between physical DPI and what GetDPI is returning, FWIW. So I'm somewhat curious how it's actually being calculated... maybe I'll dig a little more.)
(In reply to Jonathan Kew (:jfkthame) from comment #6) > In that case, both the Windows and OS X implementations are currently broken > (and I think the Android one is too, although in practice it's probably > fairly close on most devices). > > If we're using a "mozmm" unit that's based on this, then I presume that must > be broken too, at least on Win7. (Where do we actually use it? Only on > mobile?) It did work when I tested it on Windows and OS X, a long time ago, on the hardware I tested it on. It's not used much because the legitimate use-cases are rare: AFAIK only "life-sized" elements and elements that are touch targets. Since touch interfaces are getting more common even for desktop OSes, I think it might be getting more useful. I'm not really sure what you mean by "logical DPI" and "physical DPI". GetDPI is meant to return the number of device pixels per physical inch.
(In reply to Robert O'Callahan (:roc) (Mozilla Corporation) from comment #7) > (In reply to Jonathan Kew (:jfkthame) from comment #6) > > In that case, both the Windows and OS X implementations are currently broken > > (and I think the Android one is too, although in practice it's probably > > fairly close on most devices). > > > > If we're using a "mozmm" unit that's based on this, then I presume that must > > be broken too, at least on Win7. (Where do we actually use it? Only on > > mobile?) > > It did work when I tested it on Windows and OS X, a long time ago, on the > hardware I tested it on. I'm sure there are some Windows configurations where it works, but it's definitely broken on some pretty "normal" hardware, and even the MSDN doc implicitly acknowledges that the API it's using will often be wildly inaccurate. > It's not used much because the legitimate use-cases are rare: AFAIK only > "life-sized" elements and elements that are touch targets. Since touch > interfaces are getting more common even for desktop OSes, I think it might > be getting more useful. FWIW, I'm not all that convinced by the "touch targets" use case; e.g. consider the Windows Metro UI, and what happens when you use the a11y option to "make everything bigger". It does exactly that - makes -everything- on the screen bigger, both "content" that's meant to be read and touch-UI targets that exist to be poked at. I think that's how people generally expect scaling to work. Similarly, I think it's expected that everything - including the touch UI targets - will be a physically a bit smaller on a 10" screen than on a 12" one. Compare the iPad and iPad mini. They have exactly the same UI - in terms of pixels, not physical dimensions. > > I'm not really sure what you mean by "logical DPI" and "physical DPI". "Logical DPI" is Windows terminology, no? E.g., DisplayProperties.logicalDPI. Variable according to display scaling options chosen, to make everything larger or smaller. Physical DPI would be the (fixed) property of the actual hardware. > GetDPI is meant to return the number of device pixels per physical inch. But it doesn't in fact do that on OS X, where the mozmm unit ends up being accurate (at least in my testing). Or at least, whether it does depends what you actually mean by "device pixels". What GetDPI is returning there is the number of pixels per inch in the backing store of the OpenGL surfaces, Quartz surfaces, whatever. This is -not- necessarily the same as device pixels (which I understand to refer to the actual hardware pixels in the LCD), depending on the scaling chosen in the Display preferences. The confusion in the terminology there, though, is a separate issue from the fact that GetDPI on Windows is often broken, depending on the hardware/drivers, and therefore mozmm is also broken there.
(In reply to Jonathan Kew (:jfkthame) from comment #8) > FWIW, I'm not all that convinced by the "touch targets" use case; e.g. > consider the Windows Metro UI, and what happens when you use the a11y option > to "make everything bigger". It does exactly that - makes -everything- on > the screen bigger, both "content" that's meant to be read and touch-UI > targets that exist to be poked at. I think that's how people generally > expect scaling to work. I haven't used Metro so I don't know how that option works. Certainly the "visual zooming" that mobile browsers do should just scale everything with a transform. That's a different feature to the "full zoom" that changes the device-pixel-to-CSS-pixel ratio. > Similarly, I think it's expected that everything - including the touch UI > targets - will be a physically a bit smaller on a 10" screen than on a 12" > one. Compare the iPad and iPad mini. They have exactly the same UI - in > terms of pixels, not physical dimensions. I think it's a bad decision to just scale every element of the UI uniformly, even if Apple does it and users are used to it. If UI designers want an element to be a good touch target but no bigger, then either it'll be too small on the 10" screen or wasting real estate on the 12" screen. I think there's some interest from FirefoxOS people in using mozmm for interfaces on devices with various DPIs. > > GetDPI is meant to return the number of device pixels per physical inch. > > But it doesn't in fact do that on OS X, where the mozmm unit ends up being > accurate (at least in my testing). Or at least, whether it does depends what > you actually mean by "device pixels". > > What GetDPI is returning there is the number of pixels per inch in the > backing store of the OpenGL surfaces, Quartz surfaces, whatever. That's what Gecko means by device pixels. I know you know this :-). > The confusion in the terminology there, though, is a separate issue from the > fact that GetDPI on Windows is often broken, depending on the > hardware/drivers, and therefore mozmm is also broken there. Sure, but we often have to make best-effort calculations. If there's a better API on Windows, we should use it.
(In reply to Robert O'Callahan (:roc) (Mozilla Corporation) from comment #9) > I think it's a bad decision to just scale every element of the UI uniformly, > even if Apple does it and users are used to it. If UI designers want an > element to be a good touch target but no bigger, then either it'll be too > small on the 10" screen or wasting real estate on the 12" screen. I'm not sure that's really true, in practice. A good size for a touch target depends very much on the context in which it occurs. On a smaller screen, people tend (and expect) to use more precise actions. Touch targets that work well on an iPod Touch would seem unusably small on my 13" Win-Metro machine. The larger screen leads to less-precise user actions, and so targets need to be correspondingly larger. > I think there's some interest from FirefoxOS people in using mozmm for > interfaces on devices with various DPIs. Perhaps so, though I suspect using (unzoomed) CSS px - whose relationship to device pixels will vary with different hardware resolutions - would be just as reasonable. Anyway, this whole discussion doesn't really belong in this bug; the fact remains that both GetDPI and mozmm are broken for many Windows systems, so we should try to do something about that. > Sure, but we often have to make best-effort calculations. If there's a > better API on Windows, we should use it. Comment #1 points to some info that should help here, AFAICT.
(In reply to Jonathan Kew (:jfkthame) from comment #10) > (In reply to Robert O'Callahan (:roc) (Mozilla Corporation) from comment #9) > > I think it's a bad decision to just scale every element of the UI uniformly, > > even if Apple does it and users are used to it. If UI designers want an > > element to be a good touch target but no bigger, then either it'll be too > > small on the 10" screen or wasting real estate on the 12" screen. > > I'm not sure that's really true, in practice. A good size for a touch target > depends very much on the context in which it occurs. On a smaller screen, > people tend (and expect) to use more precise actions. Touch targets that > work well on an iPod Touch would seem unusably small on my 13" Win-Metro > machine. The larger screen leads to less-precise user actions, and so > targets need to be correspondingly larger. OK. Do you think then that CSS px, which increases with viewing distance, is a good way to size touch targets? > > I think there's some interest from FirefoxOS people in using mozmm for > > interfaces on devices with various DPIs. > > Perhaps so, though I suspect using (unzoomed) CSS px - whose relationship to > device pixels will vary with different hardware resolutions - would be just > as reasonable. Perhaps. > Anyway, this whole discussion doesn't really belong in this bug; the fact > remains that both GetDPI and mozmm are broken for many Windows systems, so > we should try to do something about that. Well, if we decided to remove mozmm, then I think we could just remove GetDPI instead of fixing it. Even if we accept your argument about touch targets, I still think the "life size" use case is legitimate, maybe even more useful with tablets and touch devices than it used to be. But I can't recall a single site actually trying to do that, and people could print stuff out as an alternative, so maybe it's too obscure.
(In reply to Robert O'Callahan (:roc) (Mozilla Corporation) from comment #11) > OK. Do you think then that CSS px, which increases with viewing distance, is > a good way to size touch targets? Yes, I think it's pretty reasonable. When I use the touch-screen on my Win8 Ultrabook (13"), it's more or less at arm's length, and so I don't have anywhere near the same precision that I have when using the touch-screen on my Android phone, which I hold at half the distance, and poke at much more accurately. If I were to display the same thing via a projector or on a smart whiteboard, and point at the UI targets with a 6-foot stick, I'd want them to be correspondingly larger again - both so that I can see them, and because I can't hit 10mm targets with the end of a pointer. And on a future "smart watch", with perhaps a 30mm screen, I'll either want a radically simpler UI, or I'll be using a stylus or some such that gives a much more precise touch point - in which case the physically smaller CSS px elements will be fine. (Obviously (IMO), extreme differences in form factor are usually best served by different UI designs, but ignoring that aspect for now, I think touch targets that are sized in terms of CSS px should work pretty well across a reasonable range of scales.) > > > > I think there's some interest from FirefoxOS people in using mozmm for > > > interfaces on devices with various DPIs. > > > > Perhaps so, though I suspect using (unzoomed) CSS px - whose relationship to > > device pixels will vary with different hardware resolutions - would be just > > as reasonable. > > Perhaps. > > > Anyway, this whole discussion doesn't really belong in this bug; the fact > > remains that both GetDPI and mozmm are broken for many Windows systems, so > > we should try to do something about that. > > Well, if we decided to remove mozmm, then I think we could just remove > GetDPI instead of fixing it. > > Even if we accept your argument about touch targets, I still think the "life > size" use case is legitimate, maybe even more useful with tablets and touch > devices than it used to be. But I can't recall a single site actually trying > to do that, and people could print stuff out as an alternative, so maybe > it's too obscure. Maybe - I'm not really sure what I think about that use-case. As you suggest, there doesn't seem to be a great deal of demand. Though maybe B2G will shift that a bit. Right now, I wouldn't be surprised if you can get a native-code "ruler app" for the iPhone, for example, but that would naturally depend on exact knowledge of the hardware. (Sure enough: e.g. http://ruler.iphonemarks.com/ - not even native-code, actually.) Creating an equivalent for B2G devices would require a reliable mozmm.
OK, I think you just convinced me to keep it :-)
(Now that I think of it, a few Web devs complained when we broken the "CSS mm == physical mm" invariant because it meant their "ruler" Web pages didn't work ... although I think that was probably just orneriness.)
Severity: normal → S3
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: