macOS/QuickLook: support rich thumbnail #107072

Open
Ankit Meel wants to merge 10 commits from ankitm/blender:ankitm/2ql into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
5 changed files with 45 additions and 67 deletions
Showing only changes of commit 0263e6ab40 - Show all commits

View File

@ -4,6 +4,8 @@
<dict> <dict>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>en</string> <string>en</string>
<key>CFBundleName</key>
<string>blender-thumbnailer</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>blender_thumbnailer</string> <string>blender_thumbnailer</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
@ -12,22 +14,19 @@
<string>org.blenderfoundation.blender.thumbnailer</string> <string>org.blenderfoundation.blender.thumbnailer</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key>
<string>blender-thumbnailer</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>XPC!</string> <string>XPC!</string>
<key>CFBundleShortVersionString</key>
<!-- TODO automate -->
<string>3.6.0</string>
<key>CFBundleSupportedPlatforms</key> <key>CFBundleSupportedPlatforms</key>
<array> <array>
<string>MacOSX</string> <string>MacOSX</string>
</array> </array>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<!-- TODO automate --> <string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
<string>3.6.0 2023-04-18</string> <key>CFBundleGetInfoString</key>
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}, Blender Foundation</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<!-- TODO automate -->
<string>10.15</string> <string>10.15</string>
<key>NSExtension</key> <key>NSExtension</key>
<dict> <dict>
@ -35,15 +34,18 @@
<dict> <dict>
<key>QLSupportedContentTypes</key> <key>QLSupportedContentTypes</key>
<array> <array>
<!-- The supported file UTIs. Not inherited from parent bundle. -->
<string>org.blenderfoundation.blender.file</string> <string>org.blenderfoundation.blender.file</string>
</array> </array>
<key>QLThumbnailMinimumDimension</key> <key>QLThumbnailMinimumDimension</key>
<integer>10</integer> <integer>0</integer>
</dict> </dict>
<key>NSExtensionPointIdentifier</key> <key>NSExtensionPointIdentifier</key>
<string>com.apple.quicklook.thumbnail</string> <string>com.apple.quicklook.thumbnail</string>
<key>NSExtensionPrincipalClass</key> <key>NSExtensionPrincipalClass</key>
<!-- Must be the same as the class implementing the reply method. -->
<string>ThumbnailProvider</string> <string>ThumbnailProvider</string>
<!-- Shows checkbox in System Preferences. -->

Is this required, and common for these kinds of thumbnails generators? I don't see it for any other apps, but maybe I just happen to not have any apps that do this.

Is this required, and common for these kinds of thumbnails generators? I don't see it for any other apps, but maybe I just happen to not have any apps that do this.

Not required and not sure about commonness. But since there's no other way to disable it, I added it.

I could remove the key from plist and let the system decide.

Not required and not sure about commonness. But since there's no other way to disable it, I added it. I could remove the key from plist and let the system decide.

I'd leave it out and let the system decide.

I'd leave it out and let the system decide.
<key>com.apple.showsInExtensionsManager</key> <key>com.apple.showsInExtensionsManager</key>
<true/> <true/>
</dict> </dict>

View File

@ -2,10 +2,12 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<!-- Extension must be codesigned even locally. -->
<key>com.apple.security.app-sandbox</key> <key>com.apple.security.app-sandbox</key>
<true/> <true/>
<key>com.apple.security.files.user-selected.read-only</key> <key>com.apple.security.files.user-selected.read-only</key>
<true/> <true/>
ankitm marked this conversation as resolved Outdated

Inconsistent indentation.

Inconsistent indentation.
<!-- Is needed for debugging the process -->
<key>com.apple.security.get-task-allow</key> <key>com.apple.security.get-task-allow</key>
<true/> <true/>
</dict> </dict>

View File

@ -60,23 +60,13 @@ if(APPLE)
set(SRC set(SRC
src/ThumbnailProvider.mm src/ThumbnailProvider.mm
src/ThumbnailProvider.h src/ThumbnailProvider.h
src/Info.plist
) )
add_executable(blender-thumbnailer MACOSX_BUNDLE ${SRC}) add_executable(blender-thumbnailer MACOSX_BUNDLE ${SRC})
set_target_properties(blender-thumbnailer PROPERTIES
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/src/Info.plist
MACOSX_FRAMEWORK_IDENTIFIER org.blenderfoundation.blender.thumbnailer
)
set_target_properties(blender-thumbnailer PROPERTIES
BUNDLE_EXTENSION appex
BL_CODESIGN_ENTITLEMENTS_DEBUG ${CMAKE_CURRENT_SOURCE_DIR}/src/ThumbnailExtensionMacOS.entitlements.Debug
BL_CODESIGN_ENTITLEMENTS_RELEASE ${CMAKE_CURRENT_SOURCE_DIR}/src/ThumbnailExtensionMacOS.entitlements.Release
)
setup_platform_linker_flags(blender-thumbnailer) setup_platform_linker_flags(blender-thumbnailer)
target_link_libraries(blender-thumbnailer target_link_libraries(blender-thumbnailer
bf_blenlib bf_blenlib
# For main function # Avoid linker error about undefined _main symbol.
"-e _NSExtensionMain" "-e _NSExtensionMain"
"-framework QuickLookThumbnailing" "-framework QuickLookThumbnailing"
) )

View File

@ -3,64 +3,50 @@
/** /**
* This section intends to list the important steps for creating a thumbnail extension. * This section intends to list the important steps for creating a thumbnail extension.
* qlgenerator has been deprecated. App extensions are the way forward. * qlgenerator has been deprecated and removed in platforms we support. App extensions are the way
* Process goes something like this: * forward. But there's little guidance on how to do it outside Xcode.
*
* The process of thumbnail generation goes something like this:
* 1. If an app is launched, or is registered with lsregister, its plugins also get registered. * 1. If an app is launched, or is registered with lsregister, its plugins also get registered.
* 2. When a file thumbnail in Finder or QuickLook is requested, the system looks for a plugin * 2. When a file thumbnail in Finder or QuickLook is requested, the system looks for a plugin
ankitm marked this conversation as resolved Outdated

Add newline before this.

Add newline before this.
* that supports the file type UTI. * that supports the file type UTI.
* 3. The plugin is launched in a sandboxed environment and should call the handler with a reply. * 3. The plugin is launched in a sandboxed environment and should call the handler with a reply.
* *
* # Plugin Info.plist * # Plugin Info.plist
* The Info.plist file should contain the following keys: * The Info.plist file should be properly configured. See the template Info.plist
* <dict> * under release/darwin for more info.
* <key>NSExtensionAttributes</key>
* <dict>
* <key>QLSupportedContentTypes</key>
* <array>
* <!-- Exactly the file UTIs to be supported. Not inherited from parent bundle. -->
* <string>org.blenderfoundation.blender.file</string>
* </array>
* <key>QLThumbnailMinimumDimension</key>
* <!-- To be explored the impact of. -->
* <integer>10</integer>
* </dict>
* <key>NSExtensionPointIdentifier</key>
* <!--com.apple.quicklook.thumbnail for preview extensions -->
* <string>com.apple.quicklook.thumbnail</string>
* <key>NSExtensionPrincipalClass</key>
* <!-- Must be the same as in the code. -->
* <string>ThumbnailProvider</string>
* <!-- Shows checkbox in System Preferences. -->
* <key>com.apple.showsInExtensionsManager</key>
* <true/>
* </dict>
* *
* # Codesigning * # Codesigning
* - The plugin should be codesigned with entitlements at least for sandbox and read-only/ * The plugin should be codesigned with entitlements at least for sandbox and read-only/
* read-write (for access to the given file). * read-write (for access to the given file). It's needed even to run the plugin locally.
* - com.apple.security.get-task-allow is required for debugging. * com.apple.security.get-task-allow is required for debugging.
* *
* # Registering the plugin * # Registering the plugin
* The plugin should be registered with lsregister. Either by calling lsregister or by launching * The plugin should be registered with lsregister. Either by calling lsregister or by launching
* the parent app. * the parent app.
* /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister
* *
* # Debugging * # Debugging
* Since read-only entitlement is there, creating files to log is not possible. So NSLOG and * Since read-only entitlement is there, creating files to log is not possible. So NSLOG and
* viewing it in Console.app (after triggering a thumbnail) is the * viewing it in Console.app (after triggering a thumbnail) is the way to go. Interesting processes
* way to go. Interesting processes are: qlmanage, quicklookd, kernel, * are: qlmanage, quicklookd, kernel, blender-thumbnailer, secinitd,
* blender-thumbnailer, secinitd, com.apple.quicklook.ThumbnailsAgent * com.apple.quicklook.ThumbnailsAgent
* *
* LLDB/ Xcode, Other debugging tools can be used to debug the plugin. /usr/bin/qlmanage * LLDB/ Xcode etc., debuggers can be used to get extra logs than CLI invocation but breakpoints
* is the target. Other args follow. * still are a painpoint. /usr/bin/qlmanage is the target executable. Other args to qlmanage
* follow.
* *
* # Troubleshooting * # Troubleshooting
* - Is it registered with lsregister and there isn't a conflict with another plugin taking
* precendence?
* - Is it codesigned and sandboxed?
* - Sometimes blender-thumbnailer running in background can be killed. * - Sometimes blender-thumbnailer running in background can be killed.
* - qlmanage -r && killall Finder * - qlmanage -r && killall Finder
* - lsregister -dump | grep Blender.app/
* *
* # Triggering a thumbnail * # Triggering a thumbnail
* - qlmanage -t /path/to/file.blend * - qlmanage -t /path/to/file.blend
* - qlmanage -t -s 512 -o /tmp/ /path/to/file.blend * - qlmanage -t -s 512 -o /tmp/ /path/to/file.blend
*
*/ */
@implementation ThumbnailProvider @implementation ThumbnailProvider

View File

@ -1317,6 +1317,16 @@ elseif(APPLE)
MACOSX_BUNDLE_LONG_VERSION_STRING "${BLENDER_VERSION}.${BLENDER_VERSION_PATCH} ${BLENDER_DATE}" MACOSX_BUNDLE_LONG_VERSION_STRING "${BLENDER_VERSION}.${BLENDER_VERSION_PATCH} ${BLENDER_DATE}"
) )
if(WITH_BLENDER_THUMBNAILER)
set(OSX_THUMBNAILER_SOURCEDIR ${OSX_APP_SOURCEDIR}/Contents/PlugIns/blender-thumbnailer.appex)
set_target_properties(blender-thumbnailer PROPERTIES
BUNDLE_EXTENSION appex
MACOSX_BUNDLE_INFO_PLIST ${OSX_THUMBNAILER_SOURCEDIR}/Contents/Info.plist
MACOSX_BUNDLE_SHORT_VERSION_STRING "${BLENDER_VERSION}.${BLENDER_VERSION_PATCH}"
MACOSX_BUNDLE_LONG_VERSION_STRING "${BLENDER_VERSION}.${BLENDER_VERSION_PATCH} ${BLENDER_DATE}"
)
endif()
# Gather the date in finder-style. # Gather the date in finder-style.
execute_process( execute_process(
COMMAND date "+%m/%d/%Y/%H:%M" COMMAND date "+%m/%d/%Y/%H:%M"
@ -1354,9 +1364,7 @@ elseif(APPLE)
TARGETS blender-thumbnailer TARGETS blender-thumbnailer
DESTINATION Blender.app/Contents/Plugins DESTINATION Blender.app/Contents/Plugins
) )
get_target_property(BL_CODESIGN_ENTITLEMENTS_DEBUG blender-thumbnailer BL_CODESIGN_ENTITLEMENTS_DEBUG) set(BL_CODESIGN_ENTITLEMENTS "${OSX_APP_SOURCEDIR}/../thumbnail_entitlements.plist")
ankitm marked this conversation as resolved Outdated

Not using relative path is more clear I think, their relative location has no particular importance: ${CMAKE_SOURCE_DIR}/release/darwin/thumbnail_entitlements.plist.

Don't invent new BL_ prefix for variable names, suggest to use THUMBNAIL_ENTITLEMENTS

Not using relative path is more clear I think, their relative location has no particular importance: `${CMAKE_SOURCE_DIR}/release/darwin/thumbnail_entitlements.plist`. Don't invent new `BL_` prefix for variable names, suggest to use `THUMBNAIL_ENTITLEMENTS`
get_target_property(BL_CODESIGN_ENTITLEMENTS_RELEASE blender-thumbnailer BL_CODESIGN_ENTITLEMENTS_RELEASE)
set(BL_CODESIGN_ENTITLEMENTS "$<IF:$<CONFIG:Debug>,${BL_CODESIGN_ENTITLEMENTS_DEBUG},${BL_CODESIGN_ENTITLEMENTS_RELEASE}>")
install(CODE install(CODE

Does this now run on every make install? That would slow down incremental builds. Is there a way to make it run only when the appex is updated?

Does this now run on every `make install`? That would slow down incremental builds. Is there a way to make it run only when the appex is updated?
Review
time codesign --entitlements release/darwin/thumbnail_entitlements.plist --force --deep --sign - ../build_darwin_debug_lite/bin/Blender.app/Contents/Plugins/blender-thumbnailer.appex
../build_darwin_debug_lite/bin/Blender.app/Contents/Plugins/blender-thumbnailer.appex: replacing existing signature
codesign --entitlements release/darwin/thumbnail_entitlements.plist --force    0.02s user 0.02s system 72% cpu 0.053 total

insignificant
Even my poor machine makes it unnoticeable. We aren't signing the full blender.app.

``` time codesign --entitlements release/darwin/thumbnail_entitlements.plist --force --deep --sign - ../build_darwin_debug_lite/bin/Blender.app/Contents/Plugins/blender-thumbnailer.appex ../build_darwin_debug_lite/bin/Blender.app/Contents/Plugins/blender-thumbnailer.appex: replacing existing signature codesign --entitlements release/darwin/thumbnail_entitlements.plist --force 0.02s user 0.02s system 72% cpu 0.053 total ``` insignificant Even my poor machine makes it unnoticeable. We aren't signing the full blender.app.
"execute_process(COMMAND codesign "execute_process(COMMAND codesign
--deep --force --sign - --entitlements \"${BL_CODESIGN_ENTITLEMENTS}\" --timestamp=none --deep --force --sign - --entitlements \"${BL_CODESIGN_ENTITLEMENTS}\" --timestamp=none
@ -1695,16 +1703,6 @@ execute_process(\
endif() endif()
endif() endif()
if(APPLE)
# Register with lsregister
install(
CODE "
execute_process(COMMAND /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister
-f -R -trusted
\"${EXECUTABLE_OUTPUT_PATH}/Blender.app\"
)"
)
endif()
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Post-install script # Post-install script