I’m on a 2017 Macbook Pro with retina display. When I open a window, with or without fullscreening, it centers it on the top-left corner of the screen. This occurs whether I open the window on screen index 0 or 1 (notably, when opening on screen index 1, it puts it in the top left of THAT screen). Everything is on the pyglet backend. By default new windows are opened at 0,0, and it looks like that’s being mapped to the top-left corner now instead of the center of the screen. Notably, it does not do this on 1.85.6 on the same displays. I can’t figure out where that’s actually determined in the code or how it changed from 1.85.6. Help?
This issue is still present in 1.90.1. Where exactly does PsychoPy determine where to open the window? I can’t find it in the relevant class.
OK. I have located the source of the bug. I have an inelegant fix, but I still don’t understand where it’s coming from. @jon your input would be appreciated.
The key issue is some kind of weird interplay between lines 164 and 204-206 in pygletbackend.py in the backends sub-folder of visual.window.
Line 164 sets the size of the window for retina displays. The output is, effectively, double whatever you put in to your PsychoPy code. So if you try to make a 1440 x 1080 window, this line will output a win.size array of [2880, 2160]. This is necessary for stimuli to display properly on retina displays.
Lines 204-206 computes where to position the window if no position is provided when the window is created.
if not win.pos: # work out where the centre should be win.pos = [(thisScreen.width - win.size) / 2, (thisScreen.height - win.size) / 2]
I have a retina display that tells thisScreen.width that its width is 1440 pixels. Ah, but win.size on a retina display has been doubled. So, it’s subtracting 2880 from 1440, dividing that by 2, and putting the top-left corner of the window at x = -720.
What I can’t figure out is why thisScreen is reporting in “corrected” retina pixels while everything that uses win.size needs “actual” (doubled) pixels. The inelegant solution is to simply compute win.pos differently depending on whether win.useRetina is true. That’s what I’m going to submit as a BF shortly, but I feel like this is going to cause more problems later if we don’t figure out where it’s originating from.
Note that this only matters for non-fullscreen windows, by the way. Fullscreen windows are just fine.
I’ve pulled in your PR (thanks for your patience - there were some other things I needed to look into around the travis-ci automated test suite).
The problem here is that Apple didn’t just treat their retina displays as being simply hi-res displays with tiny pixels (I guess they were worried that people setting sizes of things by pixels would be freaked out if those things were suddenly half the size) so their solution was to refer to things in two systems: the original units (virtual pixels) where things will be the same size and then provide hooks to access the true physical pixels underneath. In fact their new step seems to be not to mention pixels at all (e.g. in System Settings)!
That leaves us with a problem. When a user requests 400x400 pixels do we now use the virtual pixels (so things look right. and because Apple suggest it, at least implicitly) or the native pixels (because these are the reality)? What I’ve gone for so far, which feels most in keeping with Apple’s own method, is to provide a window/stimulus that looks the size the user expected, but behind the scenes report the win size back in real pixels.
But maybe we should change that and go wholesale to a method of creating the window/stimuli always in true pixels (which I kinda wish Apple did in the first place)
How very Apple. I think having a user-controlled option to turn off “useRetina” might make sense for people that actually want to use the ludicrous PPI, but by default virtual pixels are going to make more sense for most users. The issue I was more concerned about was that the screen dimensions, which seem to be reported back from pyglet, are using virtual pixels while the win.size information is using true pixels. I thought about just modifying the reported screen dimensions when useRetina is true, but I was concerned that would mess with other systems that use that information. I don’t know if that comes up anywhere else, though.