cocoa API to get the image for a bundle as PNG

This commit is contained in:
Kovid Goyal
2024-07-30 10:19:17 +05:30
parent 3cc2a8c484
commit 8026947572
2 changed files with 43 additions and 4 deletions

View File

@@ -6,6 +6,7 @@
*/
#include "pytypedefs.h"
#include "state.h"
#include "cleanup.h"
#include "cocoa_window.h"
@@ -19,6 +20,9 @@
#include <crt_externs.h>
#include <objc/runtime.h>
static inline void cleanup_cfrelease(void *__p) { CFTypeRef *tp = (CFTypeRef *)__p; CFTypeRef cf = *tp; if (cf) { CFRelease(cf); } }
#define RAII_CoreFoundation(type, name, initializer) __attribute__((cleanup(cleanup_cfrelease))) type name = initializer
#if (MAC_OS_X_VERSION_MAX_ALLOWED < 101300)
#define NSControlStateValueOn NSOnState
#define NSControlStateValueOff NSOffState
@@ -453,11 +457,11 @@ live_delivered_notifications(void) {
}
static void
schedule_notification(const char *appname, const char *identifier, const char *title, const char *body, const char *image_path, int urgency) {
schedule_notification(const char *appname, const char *identifier, const char *title, const char *body, const char *image_path, int urgency) {@autoreleasepool {
UNUserNotificationCenter *center = get_notification_center_safely();
if (!center) return;
// Configure the notification's payload.
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
RAII_CoreFoundation(UNMutableNotificationContent, *content, [[UNMutableNotificationContent alloc] init]);
if (title) content.title = @(title);
if (body) content.body = @(body);
if (appname) content.threadIdentifier = @(appname);
@@ -504,8 +508,7 @@ schedule_notification(const char *appname, const char *identifier, const char *t
free(duped_ident);
});
}];
[content release];
}
}}
typedef struct {
@@ -1069,6 +1072,40 @@ cocoa_set_uncaught_exception_handler(void) {
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
}
static PyObject*
bundle_image_as_png(PyObject *self UNUSED, PyObject *args, PyObject *kw) {@autoreleasepool {
const char *b, *output_path = NULL; int is_identifier = 0; unsigned image_size = 256;
static const char* kwlist[] = {"path_or_identifier", "output_path", "image_size", "is_identifier", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kw, "s|sIp", (char**)kwlist, &b, &output_path, &image_size, &is_identifier)) return NULL;
NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; // autoreleased
NSImage *icon = nil;
if (is_identifier) {
NSURL *url = [workspace URLForApplicationWithBundleIdentifier:@(b)]; // autoreleased
if (!url) {
PyErr_Format(PyExc_KeyError, "Failed to find bundle path for identifier: %s", b); return NULL;
}
icon = [workspace iconForFile:@(url.fileSystemRepresentation)];
} else icon = [workspace iconForFile:@(b)];
if (!icon) {
PyErr_Format(PyExc_ValueError, "Failed to load icon for bundle: %s", b); return NULL;
}
NSRect r = NSMakeRect(0, 0, image_size, image_size);
RAII_CoreFoundation(CGColorSpaceRef, colorSpace, CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));
RAII_CoreFoundation(CGContextRef, cgContext, CGBitmapContextCreate(NULL, image_size, image_size, 8, 4*image_size, colorSpace, kCGBitmapByteOrderDefault|kCGImageAlphaPremultipliedLast));
NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithCGContext:cgContext flipped:NO]; // autoreleased
RAII_CoreFoundation(CGImageRef, cg, [icon CGImageForProposedRect:&r context:context hints:nil]);
RAII_CoreFoundation(NSBitmapImageRep, *rep, [[NSBitmapImageRep alloc] initWithCGImage:cg]);
NSData *png = [rep representationUsingType:NSBitmapImageFileTypePNG properties:@{NSImageCompressionFactor: @1.0}]; // autoreleased
if (output_path) {
if (![png writeToFile:@(output_path) atomically:YES]) {
PyErr_Format(PyExc_OSError, "Failed to write PNG data to %s", output_path);
return NULL;
}
return PyBytes_FromStringAndSize(NULL, 0);
}
return PyBytes_FromStringAndSize(png.bytes, png.length);
}}
static PyMethodDef module_methods[] = {
{"cocoa_get_lang", (PyCFunction)cocoa_get_lang, METH_NOARGS, ""},
{"cocoa_set_global_shortcut", (PyCFunction)cocoa_set_global_shortcut, METH_VARARGS, ""},
@@ -1079,6 +1116,7 @@ static PyMethodDef module_methods[] = {
{"cocoa_set_url_handler", (PyCFunction)cocoa_set_url_handler, METH_VARARGS, ""},
{"cocoa_set_app_icon", (PyCFunction)cocoa_set_app_icon, METH_VARARGS, ""},
{"cocoa_set_dock_icon", (PyCFunction)cocoa_set_dock_icon, METH_VARARGS, ""},
{"cocoa_bundle_image_as_png", (PyCFunction)(void(*)(void))bundle_image_as_png, METH_VARARGS | METH_KEYWORDS, ""},
{NULL, NULL, 0, NULL} /* Sentinel */
};

View File

@@ -571,6 +571,7 @@ def cocoa_send_notification(
) -> None:
pass
def cocoa_bundle_image_as_png(path_or_identifier: str, output_path: str = '', image_size: int = 256, is_identifier: bool = False) -> bytes: ...
def cocoa_remove_delivered_notification(identifier: str) -> bool: ...
def cocoa_live_delivered_notifications() -> bool: ...