I'm not sure if this is due to changes between one version of OS X and another, or if this is just broken across all versions. I'm on Lion.
If I call position, and then use its returned point as input to setWindowPositionToX:Y:, the window moves. That shouldn't happen.
Take a look at the following rewrites, which solve the problem for me. I hope, but don't have an easy way to test, that this rewrite corrects the behavior across all of SFML's supported versions of OS X.
////////////////////////////////////////////////////////////
-(NSPoint)position
{
NSRect frame = m_window.frame;
NSPoint pos = frame.origin;
// Flip for SFML window coordinate system.
pos.y += frame.size.height;
pos.y = m_window.screen.frame.size.height - pos.y;
return pos;
}
////////////////////////////////////////////////////////.
-(void)setWindowPositionToX:(unsigned int)x Y:(unsigned int)y
{
NSPoint point = NSMakePoint((int)x, (int)y);
NSScreen *screen = m_window.screen;
// Flip for SFML window coordinate system.
point.y = m_window.screen.frame.size.height - point.y;
// Place the window.
[m_window setFrameTopLeftPoint:point];
if (screen != [m_window screen]) {
// Oops. Coordinates were for a different screen. Do-over.
[self setWindowPositionToX:x Y:y];
}
}
The key differences:
position should be using the window's frame, not the OpenGL view's frame (unless there's some reasoning for using the OpenGL view that I'm missing.)
position also must account for the window's size when flipping the y coordinate.
setWindowPositionToX:Y: should be taking ints, not unsigned ints. It is legitimate for a window to have a negative X or Y, especially in the case of multiple displays (which my changes appear to support without issue).
setWindowPositionToX:Y: also needs to recognize that the coordinates may not be for the main display, and must use the proper display height for adjustment. There's probably a better way to handle that case than what I did, but what I did is working. My lazy implementation just checks to see if the repositioned window ended up on a different screen, and if so, recognizes the mistake and repeats the positioning now that m_window.screen points to the screen that the coordinates were supposed to reference.
This came up when I was trying to go from Windowed to Fullscreen to Windowed. My window would not end up in the right place after exiting Fullscreen. These changes resolve that.
EDIT:Upon further reflection, this works fine for side-by-side displays, but is still slightly deficient in a case of vertically or diagonally positioned displays. I'm doing at bit more exploration to see if I can gracefully resolve vertical positioning despite dealing with a coordinate system translation in the process.
EDIT 2:I stand corrected. (Mostly...)
Turns out this works pretty well for vertically oriented displays too. It's not perfect. If the window is placed such that it sits partially in one window's bottom right corner, and partially in another window's top left corner, the positioning will be slightly off if you use the values from a getPosition in a call to setPosition. However, this solution at least changes the problem from "happens all the time" to "happens in one edge case", and in that edge case the incorrect behavior is now much more minimal.
I think it's as close as I can get without SFML itself being more multi-display aware. I'm going to push this to my fork of SFML and open a pull request. If SFML's maintainers don't approve, that's cool.
This whole issue specifically comes up when switching between Window and Fullscreen. Since SFML dictates that the window has to be recreated each time, there's no internal state on the window itself by which a previous size and location can be remembered. The application itself is left with the responsibility of implementing a fullscreen switch and ensuring predictable behavior. This change at least makes getPosition and setPosition behave in a more predictable manner.