From 576c25b3eab8d3a3501379801fac8c934efe624a Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 8 Mar 2021 11:27:06 +0100 Subject: [PATCH 01/10] makewheel: Re-sign arm64 macOS binaries after removing signature Fixes #1123 --- makepanda/makewheel.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/makepanda/makewheel.py b/makepanda/makewheel.py index adf691a559..a8fbb1d3e3 100644 --- a/makepanda/makewheel.py +++ b/makepanda/makewheel.py @@ -443,9 +443,14 @@ class WheelFile(object): subprocess.call(["install_name_tool", "-change", dep, new_dep, temp.name]) remove_signature = True - # Remove the codesign signature if we modified the library. + # Replace the codesign signature if we modified the library. if remove_signature: - subprocess.call(["codesign", "--remove-signature", temp.name]) + if self.platform.endswith("_arm64"): + subprocess.call(["codesign", "-f", "-s", "-", temp.name]) + else: + subprocess.call(["codesign", "--remove-signature", temp.name]) + if self.platform.endswith("_universal2"): + subprocess.call(["codesign", "-a", "arm64", "-s", "-", temp.name]) else: # On other unixes, we just add dependencies normally. for dep in deps: From 48fb2f721fabe3fb2a2d306fafa2de4f7dbec9d9 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 11 Mar 2021 22:19:41 +0100 Subject: [PATCH 02/10] windisplay: Fix parented child window being offset This would happen if the undecorated flag is not set; it would receive an additional offset equal to the size of the window decoration. Fixes regression presumably caused by a968caf1d207023b2820cf4c31ae3e7324ffb218 --- panda/src/windisplay/winGraphicsWindow.cxx | 36 ++++++++++++---------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/panda/src/windisplay/winGraphicsWindow.cxx b/panda/src/windisplay/winGraphicsWindow.cxx index 372cfafede..3137045871 100644 --- a/panda/src/windisplay/winGraphicsWindow.cxx +++ b/panda/src/windisplay/winGraphicsWindow.cxx @@ -298,26 +298,28 @@ set_properties_now(WindowProperties &properties) { // When switching undecorated mode, Windows will keep the window at the // current outer size, whereas we want to keep it with the configured // inner size. Store the current size and origin. - LPoint2i top_left = _properties.get_origin(); - LPoint2i bottom_right = top_left + _properties.get_size(); + if (_parent_window_handle == nullptr) { + LPoint2i top_left = _properties.get_origin(); + LPoint2i bottom_right = top_left + _properties.get_size(); - DWORD window_style = make_style(_properties); - SetWindowLong(_hWnd, GWL_STYLE, window_style); + DWORD window_style = make_style(_properties); + SetWindowLong(_hWnd, GWL_STYLE, window_style); - // Now calculate the proper size and origin with the new window style. - RECT view_rect; - SetRect(&view_rect, top_left[0], top_left[1], - bottom_right[0], bottom_right[1]); - WINDOWINFO wi; - GetWindowInfo(_hWnd, &wi); - AdjustWindowRectEx(&view_rect, wi.dwStyle, FALSE, wi.dwExStyle); + // Now calculate the proper size and origin with the new window style. + RECT view_rect; + SetRect(&view_rect, top_left[0], top_left[1], + bottom_right[0], bottom_right[1]); + WINDOWINFO wi; + GetWindowInfo(_hWnd, &wi); + AdjustWindowRectEx(&view_rect, wi.dwStyle, FALSE, wi.dwExStyle); - // We need to call this to ensure that the style change takes effect. - SetWindowPos(_hWnd, HWND_NOTOPMOST, view_rect.left, view_rect.top, - view_rect.right - view_rect.left, - view_rect.bottom - view_rect.top, - SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED | - SWP_NOSENDCHANGING | SWP_SHOWWINDOW); + // We need to call this to ensure that the style change takes effect. + SetWindowPos(_hWnd, HWND_NOTOPMOST, view_rect.left, view_rect.top, + view_rect.right - view_rect.left, + view_rect.bottom - view_rect.top, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED | + SWP_NOSENDCHANGING | SWP_SHOWWINDOW); + } } if (properties.has_title()) { From c1c2183561c7b2b324baa7e471411160f6891951 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 11 Mar 2021 22:30:35 +0100 Subject: [PATCH 03/10] windisplay: Parented child windows are implicitly undecorated I'm pretty sure applying these styles does nothing anyway, but we shouldn't specify them. --- panda/src/windisplay/winGraphicsWindow.cxx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/panda/src/windisplay/winGraphicsWindow.cxx b/panda/src/windisplay/winGraphicsWindow.cxx index 3137045871..2fc41c5038 100644 --- a/panda/src/windisplay/winGraphicsWindow.cxx +++ b/panda/src/windisplay/winGraphicsWindow.cxx @@ -979,12 +979,12 @@ make_style(const WindowProperties &properties) { if (properties.get_fullscreen()) { window_style |= WS_POPUP | WS_SYSMENU; - } else { - if (_parent_window_handle) { - window_style |= WS_CHILD; - } else { - window_style |= WS_POPUP; - } + } + else if (_parent_window_handle) { + window_style |= WS_CHILD; + } + else { + window_style |= WS_POPUP; if (!properties.get_undecorated()) { window_style |= (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX); From 7f4d030032ec5637cc2f966ec39fdff958faa19a Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 11 Mar 2021 23:29:13 +0100 Subject: [PATCH 04/10] windisplay: Fix some situations in which the mouse could stay confined Fixes #1115 --- panda/src/windisplay/winGraphicsWindow.cxx | 37 ++++++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/panda/src/windisplay/winGraphicsWindow.cxx b/panda/src/windisplay/winGraphicsWindow.cxx index 2fc41c5038..1e8921ec03 100644 --- a/panda/src/windisplay/winGraphicsWindow.cxx +++ b/panda/src/windisplay/winGraphicsWindow.cxx @@ -434,7 +434,9 @@ set_properties_now(WindowProperties &properties) { break; case WindowProperties::M_confined: - if (confine_cursor()) { + // If we are not the foreground window, we defer confining the cursor + // until we are. + if (GetForegroundWindow() != _hWnd || confine_cursor()) { _properties.set_mouse_mode(WindowProperties::M_confined); } break; @@ -475,6 +477,11 @@ close_window() { set_cursor_out_of_window(); DestroyWindow(_hWnd); + if (_properties.has_mouse_mode() && + _properties.get_mouse_mode() == WindowProperties::M_confined) { + ClipCursor(nullptr); + } + if (is_fullscreen()) { // revert to default display mode. do_fullscreen_disable(); @@ -758,12 +765,6 @@ do_reshape_request(int x_origin, int y_origin, bool has_origin, view_rect.bottom - view_rect.top, flags); - // If we are in confined mode, we must update the clip region. - if (_properties.has_mouse_mode() && - _properties.get_mouse_mode() == WindowProperties::M_confined) { - confine_cursor(); - } - handle_reshape(); return true; } @@ -813,6 +814,28 @@ handle_reshape() { return; } + // If we are in confined mode, we must update the clip region. However, + // we ony do that if the cursor in currently inside the window, to properly + // handle the case where someone is resizing the window straight after + // switching to it (you can do this if you press the start menu key to + // deactive the window, and then trying to resize it) + if (_properties.has_mouse_mode() && + _properties.get_mouse_mode() == WindowProperties::M_confined && + _hWnd == GetForegroundWindow()) { + + POINT cpos; + if (GetCursorPos(&cpos) && PtInRect(&view_rect, cpos)) { + windisplay_cat.info() + << "ClipCursor() to " << view_rect.left << "," << view_rect.top + << " to " << view_rect.right << "," << view_rect.bottom << endl; + + if (!ClipCursor(&view_rect)) { + windisplay_cat.warning() + << "Failed to re-confine cursor to window.\n"; + } + } + } + WindowProperties properties; properties.set_size((view_rect.right - view_rect.left), (view_rect.bottom - view_rect.top)); From eca18f3c19a86f11d831395aab35a2ad58098ffa Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 11 Mar 2021 23:49:09 +0100 Subject: [PATCH 05/10] windisplay: Don't try to activate window when resizing/moving it This fixes an issue where if you quickly alt-tab when the window opens, it receives a WM_ACTIVATE event and thinks it continues to be active, which causes issues with applying cursor confinement. I think you can probably still reproduce that issue, but your reflexes have to be significantly more ninja now. --- panda/src/windisplay/winGraphicsWindow.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda/src/windisplay/winGraphicsWindow.cxx b/panda/src/windisplay/winGraphicsWindow.cxx index 1e8921ec03..624c354781 100644 --- a/panda/src/windisplay/winGraphicsWindow.cxx +++ b/panda/src/windisplay/winGraphicsWindow.cxx @@ -749,7 +749,7 @@ do_reshape_request(int x_origin, int y_origin, bool has_origin, GetWindowInfo(_hWnd, &wi); AdjustWindowRectEx(&view_rect, wi.dwStyle, FALSE, wi.dwExStyle); - UINT flags = SWP_NOZORDER | SWP_NOSENDCHANGING; + UINT flags = SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_NOACTIVATE; if (has_origin) { x_origin = view_rect.left; From 84051384b89d964d775d02c9f05214f7208f3803 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 12 Mar 2021 00:36:42 +0100 Subject: [PATCH 06/10] dist: Some work on getting PyQt5 dlls found on Windows There is more work to do to get PyQt5 to work, however. --- direct/src/dist/commands.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index 68d01d97ce..07ae779786 100644 --- a/direct/src/dist/commands.py +++ b/direct/src/dist/commands.py @@ -117,10 +117,13 @@ PACKAGE_DATA_DIRS = { # Some dependencies have extra directories that need to be scanned for DLLs. # This dictionary maps wheel basenames (ie. the part of the .whl basename -# before the first hyphen) to a list of directories inside the .whl. +# before the first hyphen) to a list of tuples, the first value being the +# directory inside the wheel, the second being which wheel to look in (or +# None to look in its own wheel). PACKAGE_LIB_DIRS = { - 'scipy': ['scipy/extra-dll'], + 'scipy': [('scipy/extra-dll', None)], + 'PyQt5': [('PyQt5/Qt5/bin', 'PyQt5_Qt5')], } # site.py for Python 2. @@ -731,8 +734,13 @@ class build_apps(setuptools.Command): # Also look for more specific per-package cases, defined in # PACKAGE_LIB_DIRS at the top of this file. extra_dirs = PACKAGE_LIB_DIRS.get(whl_name, []) - for extra_dir in extra_dirs: - search_path.append(os.path.join(whl, extra_dir.replace('/', os.path.sep))) + for extra_dir, search_in in extra_dirs: + if not search_in: + search_path.append(os.path.join(whl, extra_dir.replace('/', os.path.sep))) + else: + for whl2 in wheelpaths: + if os.path.basename(whl2).startswith(search_in + '-'): + search_path.append(os.path.join(whl2, extra_dir.replace('/', os.path.sep))) return search_path From 209e031964e7b5a316aedc9f59b4cba3f061513a Mon Sep 17 00:00:00 2001 From: LD Date: Sat, 13 Mar 2021 12:44:20 +0100 Subject: [PATCH 07/10] cocoadisplay: Test all the fullscreen modes to find the valid one Closes #1039 --- panda/src/cocoadisplay/cocoaGraphicsWindow.h | 2 +- panda/src/cocoadisplay/cocoaGraphicsWindow.mm | 130 +++++++++++++++--- 2 files changed, 114 insertions(+), 18 deletions(-) diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.h b/panda/src/cocoadisplay/cocoaGraphicsWindow.h index bec277e1b6..5ae8de5a03 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.h +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.h @@ -68,7 +68,7 @@ protected: virtual bool open_window(); #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 - CGDisplayModeRef find_display_mode(int width, int height); + CFMutableArrayRef find_display_modes(int width, int height); bool do_switch_fullscreen(CGDisplayModeRef mode); #else CFDictionaryRef find_display_mode(int width, int height); diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm index ace8a4973b..11266cc173 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm @@ -629,10 +629,36 @@ open_window() { if (_properties.get_fullscreen()) { // Change the display mode. #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 - CGDisplayModeRef mode; + CFMutableArrayRef modes; + + modes = find_display_modes(_properties.get_x_size(), + _properties.get_y_size()); + + if (CFArrayGetCount(modes) > 0) { + bool switched = false; + for (CFIndex i = 0; i < CFArrayGetCount(modes); i++) { + CGDisplayModeRef mode = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); + if (do_switch_fullscreen(mode)) { + switched = true; + break; + } + } + CFRelease(modes); + + if (!switched) { + cocoadisplay_cat.error() + << "Failed to change display mode.\n"; + return false; + } + + } else { + cocoadisplay_cat.error() + << "Could not find a suitable display mode!\n"; + CFRelease(modes); + return false; + } #else CFDictionaryRef mode; -#endif mode = find_display_mode(_properties.get_x_size(), _properties.get_y_size()); @@ -647,6 +673,7 @@ open_window() { << "Failed to change display mode.\n"; return false; } +#endif } // Make the context current. @@ -793,11 +820,50 @@ set_properties_now(WindowProperties &properties) { } #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 - CGDisplayModeRef mode; + CFMutableArrayRef modes = find_display_modes(width, height); + + if (CFArrayGetCount(modes) > 0) { + bool switched = false; + for (CFIndex i = 0; i < CFArrayGetCount(modes); i++) { + CGDisplayModeRef mode = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); + if (do_switch_fullscreen(mode)) { + switched = true; + break; + } + } + + if (switched) { + 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:NSMainMenuWindowLevel+1]; + [_window makeKeyAndOrderFront:nil]; + } + + // We've already set the size property this way; clear it. + properties.clear_size(); + _properties.set_size(width, height); + properties.clear_origin(); + _properties.set_origin(0, 0); + properties.clear_fullscreen(); + _properties.set_fullscreen(true); + + } else { + cocoadisplay_cat.error() + << "Failed to change display mode.\n"; + } + } else { + cocoadisplay_cat.error() + << "Could not find a suitable display mode with size " << width + << "x" << height << "!\n"; + } + CFRelease(modes); #else CFDictionaryRef mode; -#endif - mode = find_display_mode(width, height); if (mode == NULL) { @@ -829,6 +895,7 @@ set_properties_now(WindowProperties &properties) { cocoadisplay_cat.error() << "Failed to change display mode.\n"; } +#endif } else { do_switch_fullscreen(NULL); @@ -875,11 +942,36 @@ set_properties_now(WindowProperties &properties) { } else { #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 - CGDisplayModeRef mode = find_display_mode(width, height); + CFMutableArrayRef modes = find_display_modes(width, height); + + if (CFArrayGetCount(modes) > 0) { + bool switched = false; + for (CFIndex i = 0; i < CFArrayGetCount(modes); i++) { + CGDisplayModeRef mode = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); + if (do_switch_fullscreen(mode)) { + switched = true; + break; + } + } + + if (switched) { + // Yay! Our resolution has changed. + _properties.set_size(width, height); + properties.clear_size(); + } else { + cocoadisplay_cat.error() + << "Failed to change display mode.\n"; + } + + } else { + cocoadisplay_cat.error() + << "Could not find a suitable display mode with size " << width + << "x" << height << "!\n"; + } + CFRelease(modes); + #else CFDictionaryRef mode = find_display_mode(width, height); -#endif - if (mode == NULL) { cocoadisplay_cat.error() << "Could not find a suitable display mode with size " << width @@ -894,6 +986,8 @@ set_properties_now(WindowProperties &properties) { cocoadisplay_cat.error() << "Failed to change display mode.\n"; } +#endif + } } @@ -1105,8 +1199,8 @@ set_properties_now(WindowProperties &properties) { * NULL if none was found. */ #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 -CGDisplayModeRef CocoaGraphicsWindow:: -find_display_mode(int width, int height) { +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. @@ -1126,6 +1220,9 @@ find_display_mode(int width, int height) { &kCFTypeDictionaryValueCallBacks); } #endif + CFMutableArrayRef valid_modes; + valid_modes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + CFArrayRef modes = CGDisplayCopyAllDisplayModes(_display, options); if (options != NULL) { CFRelease(options); @@ -1145,7 +1242,9 @@ find_display_mode(int width, int height) { if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_14 && CGDisplayModeGetWidth(mode) == width && CGDisplayModeGetHeight(mode) == height) { - return mode; + CFArrayAppendValue(valid_modes, mode); + CGDisplayModeRelease(mode); + return valid_modes; } current_pixel_encoding = CGDisplayModeCopyPixelEncoding(mode); @@ -1176,17 +1275,14 @@ find_display_mode(int width, int height) { #endif CFStringCompare(pixel_encoding, current_pixel_encoding, 0) == kCFCompareEqualTo) { - CFRetain(mode); - CFRelease(pixel_encoding); - CFRelease(current_pixel_encoding); - CFRelease(modes); - return mode; + CFArrayAppendValue(valid_modes, mode); } + CFRelease(pixel_encoding); } CFRelease(current_pixel_encoding); CFRelease(modes); - return NULL; + return valid_modes; } #else // Version for pre-10.6. CFDictionaryRef CocoaGraphicsWindow:: From 13a764c91ec94b15dd38b0fdbaf3a6054d46598d Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 13 Mar 2021 13:09:46 +0100 Subject: [PATCH 08/10] makewheel: Don't run codesign --remove-signature on files without signature See #927 --- makepanda/makewheel.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/makepanda/makewheel.py b/makepanda/makewheel.py index a8fbb1d3e3..aaf7efa24e 100644 --- a/makepanda/makewheel.py +++ b/makepanda/makewheel.py @@ -404,6 +404,7 @@ class WheelFile(object): # Now add dependencies. On macOS, fix @loader_path references. if sys.platform == "darwin": + is_unsigned = subprocess.call(['codesign', '-d', temp.name], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) if source_path.endswith('deploy-stubw'): deps_path = '@executable_path/../Frameworks' else: @@ -444,13 +445,14 @@ class WheelFile(object): remove_signature = True # Replace the codesign signature if we modified the library. - if remove_signature: - if self.platform.endswith("_arm64"): - subprocess.call(["codesign", "-f", "-s", "-", temp.name]) - else: - subprocess.call(["codesign", "--remove-signature", temp.name]) - if self.platform.endswith("_universal2"): - subprocess.call(["codesign", "-a", "arm64", "-s", "-", temp.name]) + if self.platform.endswith("_arm64") and (is_unsigned or remove_signature): + subprocess.call(["codesign", "-f", "-s", "-", temp.name]) + elif remove_signature and not is_unsigned: + if GetVerbose(): + print("Removing code signature from {0}".format(source_path)) + subprocess.call(["codesign", "--remove-signature", temp.name]) + if self.platform.endswith("_universal2"): + subprocess.call(["codesign", "-a", "arm64", "-s", "-", temp.name]) else: # On other unixes, we just add dependencies normally. for dep in deps: From 3c9673b48ea4f239ae801f77d7f9575e6b15be91 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 13 Mar 2021 13:55:35 +0100 Subject: [PATCH 09/10] dist: Actually replace p3fmod_audio with p3openal_audio This was meant to be effected in 9e80282affb4ce19430fdd3e16d4b3ec4d49e2b8 but was not properly checked in (only the warning was shown, but the change was not actually made). --- direct/src/dist/commands.py | 1 + 1 file changed, 1 insertion(+) diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index 07ae779786..c7b646d7f7 100644 --- a/direct/src/dist/commands.py +++ b/direct/src/dist/commands.py @@ -682,6 +682,7 @@ class build_apps(setuptools.Command): # by default. Switch it up if FMOD is not included. if value not in self.plugins and value == 'p3fmod_audio' and 'p3openal_audio' in self.plugins: self.warn("Missing audio plugin p3fmod_audio referenced in PRC data, replacing with p3openal_audio") + value = 'p3openal_audio' for plugin in check_plugins: if plugin in value and plugin not in self.plugins: From dc516c5ef10352eb43dfe08cb475db71358f3f16 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 13 Mar 2021 13:56:29 +0100 Subject: [PATCH 10/10] dist: Remove aux-display lines for excluded plug-ins No point trying to load optional modules that we know aren't included. --- direct/src/dist/commands.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index c7b646d7f7..b51c2dd642 100644 --- a/direct/src/dist/commands.py +++ b/direct/src/dist/commands.py @@ -684,6 +684,11 @@ class build_apps(setuptools.Command): self.warn("Missing audio plugin p3fmod_audio referenced in PRC data, replacing with p3openal_audio") value = 'p3openal_audio' + if var == 'aux-display': + # Silently remove aux-display lines for missing plugins. + if value not in self.plugins: + continue + for plugin in check_plugins: if plugin in value and plugin not in self.plugins: useline = False