mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-27 07:03:36 -04:00
cocoadisplay: Add support for high-dpi screens (#1308)
This commit is contained in:
parent
e55bb94996
commit
ae3cbe4b12
1
direct/src/dist/commands.py
vendored
1
direct/src/dist/commands.py
vendored
@ -717,6 +717,7 @@ class build_apps(setuptools.Command):
|
||||
'CFBundlePackageType': 'APPL',
|
||||
'CFBundleSignature': '', #TODO
|
||||
'CFBundleExecutable': self.macos_main_app,
|
||||
'NSHighResolutionCapable': 'True',
|
||||
}
|
||||
|
||||
icon = self.icon_objects.get(
|
||||
|
@ -40,12 +40,36 @@ CocoaGraphicsPipe(CGDirectDisplayID display) : _display(display) {
|
||||
[thread start];
|
||||
[thread autorelease];
|
||||
|
||||
// If the application is dpi-aware, iterate over all the screens to find the
|
||||
// one with our display ID and get the backing scale factor to configure the
|
||||
// detected display zoom. Otherwise the detected display zoom keeps its
|
||||
// default value of 1.0
|
||||
|
||||
if (dpi_aware) {
|
||||
NSScreen *screen;
|
||||
NSEnumerator *e = [[NSScreen screens] objectEnumerator];
|
||||
while (screen = (NSScreen *) [e nextObject]) {
|
||||
NSNumber *num = [[screen deviceDescription] objectForKey: @"NSScreenNumber"];
|
||||
if (_display == (CGDirectDisplayID) [num longValue]) {
|
||||
set_detected_display_zoom([screen backingScaleFactor]);
|
||||
if (cocoadisplay_cat.is_debug()) {
|
||||
cocoadisplay_cat.debug()
|
||||
<< "Display zoom is " << [screen backingScaleFactor] << "\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We used to also obtain the corresponding NSScreen here, but this causes
|
||||
// the application icon to start bouncing, which may be undesirable for
|
||||
// apps that will never open a window.
|
||||
|
||||
_display_width = CGDisplayPixelsWide(_display);
|
||||
_display_height = CGDisplayPixelsHigh(_display);
|
||||
// Although the name of these functions mention pixels, they actually return
|
||||
// display points, we use the detected display zoom to transform the values
|
||||
// into pixels.
|
||||
_display_width = CGDisplayPixelsWide(_display) * _detected_display_zoom;
|
||||
_display_height = CGDisplayPixelsHigh(_display) * _detected_display_zoom;
|
||||
load_display_information();
|
||||
|
||||
if (cocoadisplay_cat.is_debug()) {
|
||||
@ -64,19 +88,36 @@ load_display_information() {
|
||||
// _display_information->_device_id = CGDisplaySerialNumber(_display);
|
||||
|
||||
// Display modes
|
||||
CFDictionaryRef options = NULL;
|
||||
const CFStringRef dictkeys[] = {kCGDisplayShowDuplicateLowResolutionModes};
|
||||
const CFBooleanRef dictvalues[] = {kCFBooleanTrue};
|
||||
options = CFDictionaryCreate(NULL,
|
||||
(const void **)dictkeys,
|
||||
(const void **)dictvalues,
|
||||
1,
|
||||
&kCFCopyStringDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
size_t num_modes = 0;
|
||||
CFArrayRef modes = CGDisplayCopyAllDisplayModes(_display, NULL);
|
||||
CFArrayRef modes = CGDisplayCopyAllDisplayModes(_display, options);
|
||||
if (modes != NULL) {
|
||||
num_modes = CFArrayGetCount(modes);
|
||||
_display_information->_total_display_modes = num_modes;
|
||||
_display_information->_display_mode_array = new DisplayMode[num_modes];
|
||||
}
|
||||
if (options != NULL) {
|
||||
CFRelease(options);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_modes; ++i) {
|
||||
CGDisplayModeRef mode = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
|
||||
|
||||
_display_information->_display_mode_array[i].width = CGDisplayModeGetWidth(mode);
|
||||
_display_information->_display_mode_array[i].height = CGDisplayModeGetHeight(mode);
|
||||
if (dpi_aware) {
|
||||
_display_information->_display_mode_array[i].width = CGDisplayModeGetPixelWidth(mode);
|
||||
_display_information->_display_mode_array[i].height = CGDisplayModeGetPixelHeight(mode);
|
||||
} else {
|
||||
_display_information->_display_mode_array[i].width = CGDisplayModeGetWidth(mode);
|
||||
_display_information->_display_mode_array[i].height = CGDisplayModeGetHeight(mode);
|
||||
}
|
||||
_display_information->_display_mode_array[i].refresh_rate = CGDisplayModeGetRefreshRate(mode);
|
||||
_display_information->_display_mode_array[i].fullscreen_only = false;
|
||||
|
||||
|
@ -63,6 +63,7 @@ public:
|
||||
void handle_minimize_event(bool minimized);
|
||||
void handle_maximize_event(bool maximized);
|
||||
void handle_foreground_event(bool foreground);
|
||||
void handle_backing_change_event();
|
||||
bool handle_close_request();
|
||||
void handle_close_event();
|
||||
void handle_key_event(NSEvent *event);
|
||||
|
@ -132,13 +132,20 @@ move_pointer(int device, int x, int y) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Mouse position is expressed in screen points and not pixels, but in Panda3D
|
||||
// we are using pixel coordinates.
|
||||
// Instead of using convertPointFromBacking and have complex logic to cope with
|
||||
// the change of coordinate system, we cheat and directly use the contents scale
|
||||
// of the view layer to convert pixel coordinates into screen point coordinates.
|
||||
CGFloat contents_scale = _view.layer.contentsScale;
|
||||
if (device == 0) {
|
||||
CGPoint point;
|
||||
if (_properties.get_fullscreen()) {
|
||||
point = CGPointMake(x, y);
|
||||
point = CGPointMake(float(x) / contents_scale,
|
||||
float(y) / contents_scale);
|
||||
} else {
|
||||
point = CGPointMake(x + _properties.get_x_origin(),
|
||||
y + _properties.get_y_origin());
|
||||
point = CGPointMake((float(x) + _properties.get_x_origin()) / contents_scale,
|
||||
(float(y) + _properties.get_y_origin()) / contents_scale);
|
||||
}
|
||||
|
||||
if (CGWarpMouseCursorPosition(point) == kCGErrorSuccess) {
|
||||
@ -302,35 +309,89 @@ open_window() {
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the origin and the size of the window.
|
||||
// On macOS, screen coordinates are expressed in "points" which are independant
|
||||
// of the pixel density of the screen. Panda3D however, expresses the size and
|
||||
// origin of a window in pixels.
|
||||
// So, when opening a window, we need to convert the origin and size from pixel
|
||||
// units into point units. However, this conversion depends on the pixel density
|
||||
// of the screen, the backing scale factor.
|
||||
// As the origin and size of a window depends on the size of the screen of the
|
||||
// parent view, their size must be converted first from points to pixels.
|
||||
// If a window (or a view) is not configured to support high-dpi screen, macOS
|
||||
// will upscale the window (or view) when displayed on a high-dpi screen.
|
||||
// Therefore its backing scale factor will always be 1.0
|
||||
// In a Panda3D application, windows are always configured as high resolution
|
||||
// capable, but the view is only configured as high resolution if the dpi-aware
|
||||
// configuration flag is set.
|
||||
// If the app is not dpi-aware, we must upscale its size and origin from points
|
||||
// into pixels as the window is always high resolution capable.
|
||||
|
||||
// Center the window if coordinates were set to -1 or -2 TODO: perhaps in
|
||||
// future, in the case of -1, it should use the origin used in a previous
|
||||
// run of Panda
|
||||
|
||||
// Size of the requested window
|
||||
NSSize size = NSMakeSize(_properties.get_x_size(), _properties.get_y_size());
|
||||
NSRect container;
|
||||
CGFloat backing_scale_factor = screen.backingScaleFactor;
|
||||
if (parent_nsview != NULL) {
|
||||
container = [parent_nsview bounds];
|
||||
// Convert parent view bounds into pixel units.
|
||||
container = [parent_nsview convertRectToBacking:[parent_nsview bounds]];
|
||||
// If the app is not dpi-aware, we must convert its size from points into
|
||||
// pixels as the window is always high resolution capable
|
||||
if (!dpi_aware) {
|
||||
size = [parent_nsview convertSizeToBacking:size];
|
||||
}
|
||||
} else {
|
||||
container = [screen frame];
|
||||
container.origin = NSMakePoint(0, 0);
|
||||
container = [screen convertRectToBacking:container];
|
||||
if (!dpi_aware) {
|
||||
// Weirdly NSScreen does not respond to convertSizeToBacking, so we have to
|
||||
// create a dummy rect just for converting the window size.
|
||||
NSRect rect;
|
||||
rect.origin = NSMakePoint(0, 0);
|
||||
rect.size = size;
|
||||
rect = [screen convertRectToBacking:rect];
|
||||
size = rect.size;
|
||||
}
|
||||
}
|
||||
int x = _properties.get_x_origin();
|
||||
int y = _properties.get_y_origin();
|
||||
|
||||
// As we are converting a single value and the view is not created yet, it's
|
||||
// easier to simply use the backing scale factor and don't bother with
|
||||
// coordinate system transformations.
|
||||
if (x < 0) {
|
||||
x = floor(container.size.width / 2 - _properties.get_x_size() / 2);
|
||||
x = floor(container.size.width / 2 - size.width / 2);
|
||||
} else if (!dpi_aware) {
|
||||
x *= backing_scale_factor;
|
||||
}
|
||||
if (y < 0) {
|
||||
y = floor(container.size.height / 2 - _properties.get_y_size() / 2);
|
||||
y = floor(container.size.height / 2 - size.height / 2);
|
||||
} else if (!dpi_aware) {
|
||||
y *= backing_scale_factor;
|
||||
}
|
||||
if (dpi_aware) {
|
||||
_properties.set_origin(x, y);
|
||||
} else {
|
||||
_properties.set_origin(x / backing_scale_factor, y / backing_scale_factor);
|
||||
}
|
||||
_properties.set_origin(x, y);
|
||||
|
||||
if (_parent_window_handle == (WindowHandle *)NULL) {
|
||||
// Content rectangle
|
||||
NSRect rect;
|
||||
if (_properties.get_fullscreen()) {
|
||||
rect = container;
|
||||
rect = [screen convertRectFromBacking:container];
|
||||
} else {
|
||||
rect = NSMakeRect(x, container.size.height - _properties.get_y_size() - y,
|
||||
_properties.get_x_size(), _properties.get_y_size());
|
||||
rect = NSMakeRect(x, container.size.height - size.height - y,
|
||||
size.width, size.height);
|
||||
if (parent_nsview != NULL) {
|
||||
rect = [parent_nsview convertRectFromBacking:rect];
|
||||
} else {
|
||||
rect = [screen convertRectFromBacking:rect];
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the window decorations
|
||||
@ -388,8 +449,10 @@ open_window() {
|
||||
_parent_window_handle->attach_child(_window_handle);
|
||||
}
|
||||
|
||||
// Always disable application HiDPI support, Cocoa will do the eventual upscaling for us.
|
||||
[_view setWantsBestResolutionOpenGLSurface:NO];
|
||||
// Configure the view to be high resolution capable using the dpi-aware
|
||||
// configuration flag. If dpi-aware is false, macOS will upscale the view
|
||||
// for us.
|
||||
[_view setWantsBestResolutionOpenGLSurface:dpi_aware];
|
||||
if (_properties.has_icon_filename()) {
|
||||
NSImage *image = load_image(_properties.get_icon_filename());
|
||||
if (image != nil) {
|
||||
@ -596,22 +659,6 @@ set_properties_now(WindowProperties &properties) {
|
||||
}
|
||||
|
||||
if (switched) {
|
||||
if (_window != nil) {
|
||||
// For some reason, setting the style mask makes it give up its
|
||||
// first-responder status. And for some reason, we need to first
|
||||
// restore the window to normal level before we switch fullscreen,
|
||||
// otherwise we may get a black bar if we're currently on Z_top.
|
||||
if (_properties.get_z_order() != WindowProperties::Z_normal) {
|
||||
[_window setLevel: NSNormalWindowLevel];
|
||||
}
|
||||
if ([_window respondsToSelector:@selector(setStyleMask:)]) {
|
||||
[_window setStyleMask:NSBorderlessWindowMask];
|
||||
}
|
||||
[_window makeFirstResponder:_view];
|
||||
[_window setLevel:CGShieldingWindowLevel()];
|
||||
[_window makeKeyAndOrderFront:nil];
|
||||
}
|
||||
|
||||
// We've already set the size property this way; clear it.
|
||||
properties.clear_size();
|
||||
_properties.set_size(width, height);
|
||||
@ -684,6 +731,9 @@ set_properties_now(WindowProperties &properties) {
|
||||
NSMiniaturizableWindowMask | NSResizableWindowMask ];
|
||||
}
|
||||
[_window makeFirstResponder:_view];
|
||||
// Resize event fired by makeFirstResponder has an invalid backing scale factor
|
||||
// The actual size must be reset afterward
|
||||
handle_resize_event();
|
||||
}
|
||||
}
|
||||
|
||||
@ -705,6 +755,9 @@ set_properties_now(WindowProperties &properties) {
|
||||
NSMiniaturizableWindowMask | NSResizableWindowMask ];
|
||||
}
|
||||
[_window makeFirstResponder:_view];
|
||||
// Resize event fired by makeFirstResponder has an invalid backing scale factor
|
||||
// The actual size must be reset afterward
|
||||
handle_resize_event();
|
||||
}
|
||||
|
||||
properties.clear_undecorated();
|
||||
@ -715,10 +768,13 @@ set_properties_now(WindowProperties &properties) {
|
||||
int height = properties.get_y_size();
|
||||
|
||||
if (!_properties.get_fullscreen()) {
|
||||
// We use the view, not the window, to convert the frame size, expressed
|
||||
// in pixels, into points as the "dpi awareness" is managed by the view.
|
||||
NSSize size = [_view convertSizeFromBacking:NSMakeSize(width, height)];
|
||||
if (_window != nil) {
|
||||
[_window setContentSize:NSMakeSize(width, height)];
|
||||
[_window setContentSize:size];
|
||||
}
|
||||
[_view setFrameSize:NSMakeSize(width, height)];
|
||||
[_view setFrameSize:size];
|
||||
|
||||
if (cocoadisplay_cat.is_debug()) {
|
||||
cocoadisplay_cat.debug()
|
||||
@ -768,12 +824,14 @@ set_properties_now(WindowProperties &properties) {
|
||||
// Get the frame for the screen
|
||||
NSRect frame;
|
||||
NSRect container;
|
||||
// Note again that we are using the view to convert the frame and container
|
||||
// size from points into pixels.
|
||||
if (_window != nil) {
|
||||
NSRect window_frame = [_window frame];
|
||||
frame = [_window contentRectForFrameRect:window_frame];
|
||||
frame = [_view convertRectToBacking:[_window contentRectForFrameRect:window_frame]];
|
||||
NSScreen *screen = [_window screen];
|
||||
nassertv(screen != nil);
|
||||
container = [screen frame];
|
||||
container = [_view convertRectToBacking:[screen frame]];
|
||||
|
||||
// Prevent the centering from overlapping the Dock
|
||||
if (y < 0) {
|
||||
@ -783,8 +841,8 @@ set_properties_now(WindowProperties &properties) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
frame = [_view frame];
|
||||
container = [[_view superview] frame];
|
||||
frame = [_view convertRectToBacking:[_view frame]];
|
||||
container = [[_view superview] convertRectToBacking:[[_view superview] frame]];
|
||||
}
|
||||
|
||||
if (x < 0) {
|
||||
@ -795,22 +853,22 @@ set_properties_now(WindowProperties &properties) {
|
||||
}
|
||||
_properties.set_origin(x, y);
|
||||
|
||||
if (!_properties.get_fullscreen()) {
|
||||
// Remember, Mac OS X coordinates are flipped in the vertical axis.
|
||||
frame.origin.x = x;
|
||||
frame.origin.y = container.size.height - y - frame.size.height;
|
||||
frame.origin.x = x;
|
||||
// Y coordinate in backing store is not flipped, but origin is still at the bottom left
|
||||
frame.origin.y = y - container.size.height;
|
||||
|
||||
if (cocoadisplay_cat.is_debug()) {
|
||||
cocoadisplay_cat.debug()
|
||||
<< "Setting window content origin to "
|
||||
<< frame.origin.x << ", " << frame.origin.y << "\n";
|
||||
}
|
||||
if (cocoadisplay_cat.is_debug()) {
|
||||
cocoadisplay_cat.debug()
|
||||
<< "Setting window content origin to "
|
||||
<< frame.origin.x << ", " << frame.origin.y << "\n";
|
||||
}
|
||||
|
||||
if (_window != nil) {
|
||||
[_window setFrame:[_window frameRectForContentRect:frame] display:NO];
|
||||
} else {
|
||||
[_view setFrame:frame];
|
||||
}
|
||||
if (_window != nil) {
|
||||
frame = [_view convertRectFromBacking:frame];
|
||||
[_window setFrame:[_window frameRectForContentRect:frame] display:NO];
|
||||
} else {
|
||||
frame = [_view convertRectFromBacking:frame];
|
||||
[_view setFrame:frame];
|
||||
}
|
||||
properties.clear_origin();
|
||||
}
|
||||
@ -957,24 +1015,20 @@ unbind_context() {
|
||||
CFMutableArrayRef CocoaGraphicsWindow::
|
||||
find_display_modes(int width, int height) {
|
||||
CFDictionaryRef options = NULL;
|
||||
// On macOS 10.15+ (Catalina), we want to select the display mode with the
|
||||
// samescaling factor as the current view to avoid cropping or scaling issues.
|
||||
// This is a workaround until HiDPI display or scaling factor is properly
|
||||
// handled. CGDisplayCopyAllDisplayModes() does not return upscaled display
|
||||
// mode unless explicitly asked with kCGDisplayShowDuplicateLowResolutionModes
|
||||
// We want to select the display mode with the same scaling factor as the
|
||||
// current view to avoid cropping or scaling issues.
|
||||
// CGDisplayCopyAllDisplayModes() does not return upscaled display modes
|
||||
// nor the current mode, unless explicitly asked with
|
||||
// kCGDisplayShowDuplicateLowResolutionModes
|
||||
// (which is undocumented...).
|
||||
bool macos_10_15_or_higher = false;
|
||||
if (@available(macOS 10.15, *)) {
|
||||
const CFStringRef dictkeys[] = {kCGDisplayShowDuplicateLowResolutionModes};
|
||||
const CFBooleanRef dictvalues[] = {kCFBooleanTrue};
|
||||
options = CFDictionaryCreate(NULL,
|
||||
(const void **)dictkeys,
|
||||
(const void **)dictvalues,
|
||||
1,
|
||||
&kCFCopyStringDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
macos_10_15_or_higher = true;
|
||||
}
|
||||
const CFStringRef dictkeys[] = {kCGDisplayShowDuplicateLowResolutionModes};
|
||||
const CFBooleanRef dictvalues[] = {kCFBooleanTrue};
|
||||
options = CFDictionaryCreate(NULL,
|
||||
(const void **)dictkeys,
|
||||
(const void **)dictvalues,
|
||||
1,
|
||||
&kCFCopyStringDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
CFMutableArrayRef valid_modes;
|
||||
valid_modes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
|
||||
|
||||
@ -985,29 +1039,39 @@ find_display_modes(int width, int height) {
|
||||
|
||||
size_t num_modes = CFArrayGetCount(modes);
|
||||
CGDisplayModeRef mode;
|
||||
|
||||
// Get the current refresh rate and pixel encoding.
|
||||
CFStringRef current_pixel_encoding;
|
||||
double refresh_rate;
|
||||
mode = CGDisplayCopyDisplayMode(_display);
|
||||
|
||||
// Calculate requested display size and pixel size
|
||||
CGSize display_size;
|
||||
CGSize pixel_size;
|
||||
if (dpi_aware) {
|
||||
pixel_size = NSMakeSize(width, height);
|
||||
display_size = [_view convertSizeFromBacking:pixel_size];
|
||||
} else {
|
||||
display_size = NSMakeSize(width, height);
|
||||
// Calculate the pixel width and height of the fullscreen mode we want using
|
||||
// the current display mode dimensions and pixel dimensions.
|
||||
size_t pixel_width = (size_t(width) * CGDisplayModeGetPixelWidth(mode)) / CGDisplayModeGetWidth(mode);
|
||||
size_t pixel_height = (size_t(height) * CGDisplayModeGetPixelHeight(mode)) / CGDisplayModeGetHeight(mode);
|
||||
pixel_size = NSMakeSize(pixel_width, pixel_height);
|
||||
}
|
||||
|
||||
// First check if the current mode is adequate.
|
||||
// This test not done for macOS 10.15 and above as the mode resolution is
|
||||
// not enough to identify a mode.
|
||||
if (!macos_10_15_or_higher &&
|
||||
CGDisplayModeGetWidth(mode) == width &&
|
||||
CGDisplayModeGetHeight(mode) == height) {
|
||||
if (CGDisplayModeGetWidth(mode) == display_size.width &&
|
||||
CGDisplayModeGetHeight(mode) == display_size.height &&
|
||||
CGDisplayModeGetPixelWidth(mode) == pixel_size.width &&
|
||||
CGDisplayModeGetPixelHeight(mode) == pixel_size.height) {
|
||||
CFArrayAppendValue(valid_modes, mode);
|
||||
CGDisplayModeRelease(mode);
|
||||
return valid_modes;
|
||||
}
|
||||
|
||||
// Get the current refresh rate and pixel encoding.
|
||||
CFStringRef current_pixel_encoding;
|
||||
double refresh_rate;
|
||||
|
||||
current_pixel_encoding = CGDisplayModeCopyPixelEncoding(mode);
|
||||
refresh_rate = CGDisplayModeGetRefreshRate(mode);
|
||||
// Calculate the pixel width and height of the fullscreen mode we want using
|
||||
// the currentdisplay mode dimensions and pixel dimensions.
|
||||
size_t expected_pixel_width = (size_t(width) * CGDisplayModeGetPixelWidth(mode)) / CGDisplayModeGetWidth(mode);
|
||||
size_t expected_pixel_height = (size_t(height) * CGDisplayModeGetPixelHeight(mode)) / CGDisplayModeGetHeight(mode);
|
||||
CGDisplayModeRelease(mode);
|
||||
|
||||
for (size_t i = 0; i < num_modes; ++i) {
|
||||
@ -1015,17 +1079,15 @@ find_display_modes(int width, int height) {
|
||||
|
||||
CFStringRef pixel_encoding = CGDisplayModeCopyPixelEncoding(mode);
|
||||
|
||||
// As explained above, we want to select the fullscreen display mode using
|
||||
// the same scaling factor, but only for MacOS 10.15+ To do this we check
|
||||
// the mode width and height but also actual pixel widh and height.
|
||||
if (CGDisplayModeGetWidth(mode) == width &&
|
||||
CGDisplayModeGetHeight(mode) == height &&
|
||||
// We select the fullscreen display mode using he same scaling factor
|
||||
// To do this we check the mode width and height but also actual pixel widh
|
||||
// and height.
|
||||
if (CGDisplayModeGetWidth(mode) == display_size.width &&
|
||||
CGDisplayModeGetHeight(mode) == display_size.height &&
|
||||
(int)(CGDisplayModeGetRefreshRate(mode) + 0.5) == (int)(refresh_rate + 0.5) &&
|
||||
(!macos_10_15_or_higher ||
|
||||
(CGDisplayModeGetPixelWidth(mode) == expected_pixel_width &&
|
||||
CGDisplayModeGetPixelHeight(mode) == expected_pixel_height)) &&
|
||||
CGDisplayModeGetPixelWidth(mode) == pixel_size.width &&
|
||||
CGDisplayModeGetPixelHeight(mode) == pixel_size.height &&
|
||||
CFStringCompare(pixel_encoding, current_pixel_encoding, 0) == kCFCompareEqualTo) {
|
||||
|
||||
if (CGDisplayModeGetRefreshRate(mode) == refresh_rate) {
|
||||
// Exact match for refresh rate, prioritize this.
|
||||
CFArrayInsertValueAtIndex(valid_modes, 0, mode);
|
||||
@ -1103,14 +1165,25 @@ do_switch_fullscreen(CGDisplayModeRef mode) {
|
||||
|
||||
NSRect frame = [[[_view window] screen] frame];
|
||||
if (cocoadisplay_cat.is_debug()) {
|
||||
NSString *str = NSStringFromRect(frame);
|
||||
NSString *str = NSStringFromSize([_view convertSizeToBacking:frame.size]);
|
||||
cocoadisplay_cat.debug()
|
||||
<< "Switched to fullscreen, screen rect is now " << [str UTF8String] << "\n";
|
||||
<< "Switched to fullscreen, screen size is now " << [str UTF8String] << "\n";
|
||||
}
|
||||
|
||||
if (_window != nil) {
|
||||
// For some reason, setting the style mask makes it give up its
|
||||
// first-responder status.
|
||||
if ([_window respondsToSelector:@selector(setStyleMask:)]) {
|
||||
[_window setStyleMask:NSBorderlessWindowMask];
|
||||
}
|
||||
[_window makeFirstResponder:_view];
|
||||
[_window setLevel:CGShieldingWindowLevel()];
|
||||
[_window makeKeyAndOrderFront:nil];
|
||||
|
||||
// Window and view frame must be updated *after* the window reconfiguration
|
||||
// or the size is not set properly !
|
||||
[_window setFrame:frame display:YES];
|
||||
[_view setFrame:NSMakeRect(0, 0, frame.size.width, frame.size.height)];
|
||||
[_view setFrame:frame];
|
||||
[_window update];
|
||||
}
|
||||
}
|
||||
@ -1252,19 +1325,25 @@ load_cursor(const Filename &filename) {
|
||||
*/
|
||||
void CocoaGraphicsWindow::
|
||||
handle_move_event() {
|
||||
// Remember, Mac OS X uses flipped coordinates
|
||||
NSRect frame;
|
||||
NSRect container;
|
||||
int x, y;
|
||||
// Again, we are using the view to convert the frame and container size from
|
||||
// points to pixels.
|
||||
if (_window == nil) {
|
||||
frame = [_view frame];
|
||||
x = frame.origin.x;
|
||||
y = [[_view superview] bounds].size.height - frame.origin.y - frame.size.height;
|
||||
frame = [_view convertRectToBacking:[_view frame]];
|
||||
container = [_view convertRectToBacking:[[_view superview] frame]];
|
||||
} else {
|
||||
frame = [_window contentRectForFrameRect:[_window frame]];
|
||||
x = frame.origin.x;
|
||||
y = [[_window screen] frame].size.height - frame.origin.y - frame.size.height;
|
||||
frame = [_view convertRectToBacking:[_window contentRectForFrameRect:[_window frame]]];
|
||||
NSScreen *screen = [_window screen];
|
||||
nassertv(screen != nil);
|
||||
container = [_view convertRectToBacking:[screen frame]];
|
||||
}
|
||||
|
||||
// Y coordinate in backing store is not flipped, but origin is still at the bottom left
|
||||
x = frame.origin.x;
|
||||
y = container.size.height + frame.origin.y;
|
||||
|
||||
if (x != _properties.get_x_origin() ||
|
||||
y != _properties.get_y_origin()) {
|
||||
|
||||
@ -1290,7 +1369,7 @@ handle_resize_event() {
|
||||
[_view setFrameSize:contentRect.size];
|
||||
}
|
||||
|
||||
NSRect frame = [_view convertRect:[_view bounds] toView:nil];
|
||||
NSRect frame = [_view convertRectToBacking:[_view bounds]];
|
||||
|
||||
WindowProperties properties;
|
||||
bool changed = false;
|
||||
@ -1403,6 +1482,22 @@ handle_foreground_event(bool foreground) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called by the window delegate when the properties of backing store of the
|
||||
* window have changed.
|
||||
*/
|
||||
void CocoaGraphicsWindow::
|
||||
handle_backing_change_event() {
|
||||
if (cocoadisplay_cat.is_debug()) {
|
||||
cocoadisplay_cat.debug() << "Backing store properties have changed\n";
|
||||
}
|
||||
// Trigger a resize event to update the window size in case the backing scale
|
||||
// factor did change.
|
||||
handle_resize_event();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called by the window delegate when the user requests to close the window.
|
||||
* This may not always be called, which is why there is also a
|
||||
@ -1693,6 +1788,13 @@ void CocoaGraphicsWindow::
|
||||
handle_mouse_moved_event(bool in_window, double x, double y, bool absolute) {
|
||||
double nx, ny;
|
||||
|
||||
// Mouse position is received in screen points and not pixels, but in Panda3D
|
||||
// we want to have the coordinates expressed in pixels.
|
||||
// Instead of using convertPointFrom/toBackingStore and have complex logic to
|
||||
// cope with the change of coordinate system, we cheat and directly use the
|
||||
// contents scale of the view layer to convert screen point into pixels and
|
||||
// vice-versa.
|
||||
CGFloat contents_scale = _view.layer.contentsScale;
|
||||
if (absolute) {
|
||||
if (cocoadisplay_cat.is_spam()) {
|
||||
if (in_window != _input->get_pointer().get_in_window()) {
|
||||
@ -1704,14 +1806,14 @@ handle_mouse_moved_event(bool in_window, double x, double y, bool absolute) {
|
||||
}
|
||||
}
|
||||
|
||||
nx = x;
|
||||
ny = y;
|
||||
nx = x * contents_scale;
|
||||
ny = y * contents_scale;
|
||||
|
||||
} else {
|
||||
// We received deltas, so add it to the current mouse position.
|
||||
PointerData md = _input->get_pointer();
|
||||
nx = md.get_x() + x;
|
||||
ny = md.get_y() + y;
|
||||
nx = md.get_x() + x * contents_scale;
|
||||
ny = md.get_y() + y * contents_scale;
|
||||
}
|
||||
|
||||
if (_properties.get_mouse_mode() == WindowProperties::M_confined
|
||||
@ -1721,11 +1823,13 @@ handle_mouse_moved_event(bool in_window, double x, double y, bool absolute) {
|
||||
nx = std::max(0., std::min((double) get_x_size() - 1, nx));
|
||||
ny = std::max(0., std::min((double) get_y_size() - 1, ny));
|
||||
|
||||
// Convert back mouse position to screen space using point units
|
||||
if (_properties.get_fullscreen()) {
|
||||
point = CGPointMake(nx, ny);
|
||||
point = CGPointMake(nx / contents_scale,
|
||||
ny / contents_scale);
|
||||
} else {
|
||||
point = CGPointMake(nx + _properties.get_x_origin(),
|
||||
ny + _properties.get_y_origin());
|
||||
point = CGPointMake((nx + _properties.get_x_origin()) / contents_scale,
|
||||
(ny + _properties.get_y_origin()) / contents_scale);
|
||||
}
|
||||
|
||||
if (CGWarpMouseCursorPosition(point) == kCGErrorSuccess) {
|
||||
|
@ -29,6 +29,7 @@ class CocoaGraphicsWindow;
|
||||
- (void)windowDidDeminiaturize:(NSNotification *)notification;
|
||||
- (void)windowDidBecomeKey:(NSNotification *)notification;
|
||||
- (void)windowDidResignKey:(NSNotification *)notification;
|
||||
- (void)windowDidChangeBackingProperties:(NSNotification *)notification;
|
||||
- (BOOL)windowShouldClose:(id)sender;
|
||||
- (void)windowWillClose:(id)sender;
|
||||
|
||||
|
@ -51,6 +51,10 @@
|
||||
_graphicsWindow->handle_foreground_event(false);
|
||||
}
|
||||
|
||||
- (void) windowDidChangeBackingProperties:(NSNotification *)notification {
|
||||
_graphicsWindow->handle_backing_change_event();
|
||||
}
|
||||
|
||||
- (BOOL) windowShouldClose:(id)sender {
|
||||
if (cocoadisplay_cat.is_debug()) {
|
||||
cocoadisplay_cat.debug()
|
||||
|
@ -21,6 +21,7 @@
|
||||
NotifyCategoryDecl(cocoadisplay, EXPCL_PANDA_COCOADISPLAY, EXPTP_PANDA_COCOADISPLAY);
|
||||
|
||||
extern ConfigVariableBool cocoa_invert_wheel_x;
|
||||
extern ConfigVariableBool dpi_aware;
|
||||
|
||||
extern EXPCL_PANDA_COCOADISPLAY void init_libcocoadisplay();
|
||||
|
||||
|
@ -32,6 +32,11 @@ ConfigVariableBool cocoa_invert_wheel_x
|
||||
("cocoa-invert-wheel-x", false,
|
||||
PRC_DESC("Set this to true to swap the wheel_left and wheel_right mouse "
|
||||
"button events, to restore to the pre-1.10.12 behavior."));
|
||||
ConfigVariableBool dpi_aware
|
||||
("dpi-aware", false,
|
||||
PRC_DESC("The default behavior on macOS is for Panda3D to use upscaling on"
|
||||
"high DPI screen. Set this to true to let the application use the"
|
||||
"actual pixel density of the screen."));
|
||||
|
||||
/**
|
||||
* Initializes the library. This must be called at least once before any of
|
||||
|
Loading…
x
Reference in New Issue
Block a user