From 069bc6263ec6c7454e0548c8e2673bb7cf2cc705 Mon Sep 17 00:00:00 2001 From: Martin Koerner Date: Sat, 21 Feb 2026 16:26:53 +0100 Subject: [PATCH 1/8] Update macOS launcher to fix missing icon --- osx/app_resources/plover_launcher.c | 33 ----------------------------- osx/app_resources/plover_launcher.m | 32 ++++++++++++++++++++++++++++ osx/make_app.sh | 2 +- 3 files changed, 33 insertions(+), 34 deletions(-) delete mode 100644 osx/app_resources/plover_launcher.c create mode 100644 osx/app_resources/plover_launcher.m diff --git a/osx/app_resources/plover_launcher.c b/osx/app_resources/plover_launcher.c deleted file mode 100644 index f26f19f24..000000000 --- a/osx/app_resources/plover_launcher.c +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include -#include -#include -#include -#include - -int main(int argc, char *argv[]) -{ - char python_path[PATH_MAX]; - char *new_argv[4 + argc]; - char *app_dir; - - // Get this app bundle directory. - app_dir = realpath(argv[0], NULL); - app_dir = dirname(dirname(app_dir)); - - // Get path to the Python interpreter. - snprintf( - python_path, sizeof (python_path), - "%s/Frameworks/Python.framework/Versions/Current/bin/python", - app_dir - ); - - // Build new arguments list, forwarding original arguments. - new_argv[0] = python_path; - new_argv[1] = "-s"; - new_argv[2] = "-m"; - new_argv[3] = "plover.scripts.dist_main"; - memcpy(new_argv + 4, argv + 1, argc * sizeof (argv[0])); - - return execv(new_argv[0], new_argv); -} diff --git a/osx/app_resources/plover_launcher.m b/osx/app_resources/plover_launcher.m new file mode 100644 index 000000000..d820a77cf --- /dev/null +++ b/osx/app_resources/plover_launcher.m @@ -0,0 +1,32 @@ +#import +#include +#include +#include + +int main(int argc, char *argv[]) { + @autoreleasepool { + char python_path[PATH_MAX]; + char *app_dir; + + // Get this app bundle directory. + app_dir = realpath(argv[0], NULL); + app_dir = dirname(dirname(app_dir)); + + // Get path to the Python interpreter. + snprintf( + python_path, sizeof(python_path), + "%s/Frameworks/Python.framework/Versions/Current/bin/python", + app_dir); + + NSString *pythonExecutable = [NSString stringWithUTF8String:python_path]; + + NSMutableArray *arguments = [NSMutableArray arrayWithObjects:@"-s", @"-m", @"plover.scripts.dist_main", nil]; + for (int i = 1; i < argc; i++) { + [arguments addObject:[NSString stringWithUTF8String:argv[i]]]; + } + + [NSTask launchedTaskWithLaunchPath:pythonExecutable arguments:arguments]; + + return NSApplicationMain(argc, (const char **)argv); + } +} diff --git a/osx/make_app.sh b/osx/make_app.sh index ba80ce656..56aa996e0 100644 --- a/osx/make_app.sh +++ b/osx/make_app.sh @@ -94,7 +94,7 @@ bootstrap_dist "$plover_wheel" "${extra_args[@]}" run bash osx/check_universal.sh "$frameworks_dir/Python.framework" "${py_version%.*}" # Create launcher. -run gcc -Wall -O2 -arch x86_64 -arch arm64 'osx/app_resources/plover_launcher.c' -o "$macos_dir/Plover" +run gcc -Wall -O2 -arch x86_64 -arch arm64 -framework Cocoa 'osx/app_resources/plover_launcher.m' -o "$macos_dir/Plover" # Copy icon. run cp 'osx/app_resources/plover.icns' "$resources_dir/plover.icns" From e908ace851da80765dfaaefa6bb29a4034ffcd1f Mon Sep 17 00:00:00 2001 From: Martin Koerner Date: Sat, 21 Feb 2026 18:44:44 +0100 Subject: [PATCH 2/8] Fix app termination on macOS --- osx/app_resources/plover_launcher.m | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osx/app_resources/plover_launcher.m b/osx/app_resources/plover_launcher.m index d820a77cf..4812c7ac6 100644 --- a/osx/app_resources/plover_launcher.m +++ b/osx/app_resources/plover_launcher.m @@ -25,7 +25,14 @@ int main(int argc, char *argv[]) { [arguments addObject:[NSString stringWithUTF8String:argv[i]]]; } - [NSTask launchedTaskWithLaunchPath:pythonExecutable arguments:arguments]; + NSTask *task = [[NSTask alloc] init]; + task.launchPath = pythonExecutable; + task.arguments = arguments; + task.terminationHandler = ^(NSTask *task) { + [NSApp terminate:nil]; + }; + + [task launch]; return NSApplicationMain(argc, (const char **)argv); } From f544520acd8e171145b9c60bb596e72942fb8517 Mon Sep 17 00:00:00 2001 From: Martin Koerner Date: Sun, 22 Feb 2026 08:37:32 +0100 Subject: [PATCH 3/8] Move launcher from NSTask to calling Python --- osx/app_resources/plover_launcher.m | 95 ++++++++++++++++++++++------- osx/make_app.sh | 3 +- 2 files changed, 74 insertions(+), 24 deletions(-) diff --git a/osx/app_resources/plover_launcher.m b/osx/app_resources/plover_launcher.m index 4812c7ac6..75765cd11 100644 --- a/osx/app_resources/plover_launcher.m +++ b/osx/app_resources/plover_launcher.m @@ -1,39 +1,88 @@ #import +#include #include #include #include int main(int argc, char *argv[]) { @autoreleasepool { - char python_path[PATH_MAX]; - char *app_dir; - - // Get this app bundle directory. - app_dir = realpath(argv[0], NULL); + char python_home[PATH_MAX]; + char app_dir_c[PATH_MAX]; + char *app_dir = realpath(argv[0], NULL); app_dir = dirname(dirname(app_dir)); + strncpy(app_dir_c, app_dir, sizeof(app_dir_c) - 1); + app_dir_c[sizeof(app_dir_c) - 1] = '\0'; - // Get path to the Python interpreter. - snprintf( - python_path, sizeof(python_path), - "%s/Frameworks/Python.framework/Versions/Current/bin/python", - app_dir); + snprintf(python_home, sizeof(python_home), "%s/Frameworks/Python.framework/Versions/Current", app_dir_c); - NSString *pythonExecutable = [NSString stringWithUTF8String:python_path]; - - NSMutableArray *arguments = [NSMutableArray arrayWithObjects:@"-s", @"-m", @"plover.scripts.dist_main", nil]; - for (int i = 1; i < argc; i++) { - [arguments addObject:[NSString stringWithUTF8String:argv[i]]]; + wchar_t *python_home_w = Py_DecodeLocale(python_home, NULL); + if (python_home_w == NULL) { + fprintf(stderr, "Fatal error: unable to decode python_home\n"); + return 1; } + Py_SetPythonHome(python_home_w); - NSTask *task = [[NSTask alloc] init]; - task.launchPath = pythonExecutable; - task.arguments = arguments; - task.terminationHandler = ^(NSTask *task) { - [NSApp terminate:nil]; - }; + // Set program name + wchar_t* program = Py_DecodeLocale(argv[0], NULL); + Py_SetProgramName(program); - [task launch]; + Py_Initialize(); + + // After this point, we are in a Python interpreter. + + // Prepend the site-packages to sys.path + char site_packages[PATH_MAX]; + snprintf(site_packages, sizeof(site_packages), "%s/lib/python3.13/site-packages", python_home); + wchar_t *site_packages_w = Py_DecodeLocale(site_packages, NULL); + PyObject* sys_path = PySys_GetObject("path"); + PyList_Insert(sys_path, 0, PyUnicode_FromWideChar(site_packages_w, -1)); + PyMem_RawFree(site_packages_w); + + // Run the main script + PyObject* pName = PyUnicode_FromString("plover.scripts.main"); + PyObject* pModule = PyImport_Import(pName); + Py_DECREF(pName); + + if (pModule != NULL) { + PyObject* pFunc = PyObject_GetAttrString(pModule, "main"); + if (pFunc && PyCallable_Check(pFunc)) { + // Prepare arguments for main() + wchar_t** py_argv = (wchar_t**)PyMem_Malloc(sizeof(wchar_t*) * argc); + for (int i = 0; i < argc; i++) { + py_argv[i] = Py_DecodeLocale(argv[i], NULL); + } + PySys_SetArgv(argc, py_argv); + + // Call main() + PyObject* pResult = PyObject_CallObject(pFunc, NULL); + + for (int i = 0; i < argc; i++) { + PyMem_RawFree(py_argv[i]); + } + PyMem_RawFree(py_argv); + + if (pResult == NULL) { + PyErr_Print(); + fprintf(stderr, "Call to main failed.\n"); + return 1; + } + Py_DECREF(pResult); + + } else { + if (PyErr_Occurred()) PyErr_Print(); + fprintf(stderr, "Cannot find function \"main\"\n"); + } + Py_XDECREF(pFunc); + Py_DECREF(pModule); + } else { + PyErr_Print(); + fprintf(stderr, "Failed to load \"plover.scripts.main\"\n"); + return 1; + } - return NSApplicationMain(argc, (const char **)argv); + Py_Finalize(); + PyMem_RawFree(python_home_w); + PyMem_RawFree(program); + return 0; } } diff --git a/osx/make_app.sh b/osx/make_app.sh index 56aa996e0..9651b1182 100644 --- a/osx/make_app.sh +++ b/osx/make_app.sh @@ -94,7 +94,8 @@ bootstrap_dist "$plover_wheel" "${extra_args[@]}" run bash osx/check_universal.sh "$frameworks_dir/Python.framework" "${py_version%.*}" # Create launcher. -run gcc -Wall -O2 -arch x86_64 -arch arm64 -framework Cocoa 'osx/app_resources/plover_launcher.m' -o "$macos_dir/Plover" +run gcc -Wall -O2 -arch x86_64 -arch arm64 -F"$appdir/Contents/Frameworks" -Wl,-rpath,@executable_path/../Frameworks -I"$py_home/include/python${py_version%.*}" -framework Cocoa -framework Python 'osx/app_resources/plover_launcher.m' -o "$macos_dir/Plover" +run install_name_tool -change "@rpath/Versions/${py_version%.*}/Python" "@rpath/Python.framework/Versions/${py_version%.*}/Python" "$macos_dir/Plover" # Copy icon. run cp 'osx/app_resources/plover.icns' "$resources_dir/plover.icns" From a9b625f3dc43f5ab5ff0c460b2d68282ab08c1e5 Mon Sep 17 00:00:00 2001 From: Martin Koerner Date: Sun, 22 Feb 2026 09:28:05 +0100 Subject: [PATCH 4/8] Hard-code PYTHONUSERBASE --- osx/app_resources/plover_launcher.m | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osx/app_resources/plover_launcher.m b/osx/app_resources/plover_launcher.m index 75765cd11..75e3a2ddc 100644 --- a/osx/app_resources/plover_launcher.m +++ b/osx/app_resources/plover_launcher.m @@ -15,6 +15,14 @@ int main(int argc, char *argv[]) { snprintf(python_home, sizeof(python_home), "%s/Frameworks/Python.framework/Versions/Current", app_dir_c); + // Set PYTHONUSERBASE to enable user plugins + char *home = getenv("HOME"); + if (home) { + char python_user_base[PATH_MAX]; + snprintf(python_user_base, sizeof(python_user_base), "%s/Library/Application Support/plover/plugins/mac", home); + setenv("PYTHONUSERBASE", python_user_base, 1); + } + wchar_t *python_home_w = Py_DecodeLocale(python_home, NULL); if (python_home_w == NULL) { fprintf(stderr, "Fatal error: unable to decode python_home\n"); From 6fb36d9d5e4af1fac50274fab2ba6e25701f7998 Mon Sep 17 00:00:00 2001 From: Martin Koerner Date: Sun, 22 Feb 2026 22:23:26 +0100 Subject: [PATCH 5/8] Add rpath for Python.framework --- osx/make_app.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osx/make_app.sh b/osx/make_app.sh index 9651b1182..b50c46305 100644 --- a/osx/make_app.sh +++ b/osx/make_app.sh @@ -94,7 +94,7 @@ bootstrap_dist "$plover_wheel" "${extra_args[@]}" run bash osx/check_universal.sh "$frameworks_dir/Python.framework" "${py_version%.*}" # Create launcher. -run gcc -Wall -O2 -arch x86_64 -arch arm64 -F"$appdir/Contents/Frameworks" -Wl,-rpath,@executable_path/../Frameworks -I"$py_home/include/python${py_version%.*}" -framework Cocoa -framework Python 'osx/app_resources/plover_launcher.m' -o "$macos_dir/Plover" +run gcc -Wall -O2 -arch x86_64 -arch arm64 -F"$appdir/Contents/Frameworks" -Wl,-rpath,@executable_path/../Frameworks -Wl,-rpath,@executable_path/../Frameworks/Python.framework -I"$py_home/include/python${py_version%.*}" -framework Cocoa -framework Python 'osx/app_resources/plover_launcher.m' -o "$macos_dir/Plover" run install_name_tool -change "@rpath/Versions/${py_version%.*}/Python" "@rpath/Python.framework/Versions/${py_version%.*}/Python" "$macos_dir/Plover" # Copy icon. From 1d34ecb45fefa4ba9093136ad9d69b52217227ba Mon Sep 17 00:00:00 2001 From: Martin Koerner Date: Thu, 26 Feb 2026 20:23:12 +0100 Subject: [PATCH 6/8] Make Babel lowercase --- pyproject.toml | 2 +- reqs/setup.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 08ab9e348..ccbf3557c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] requires = [ - "Babel", + "babel", "PySide6>=6.9.0", "setuptools>=79.0.0", "wheel", diff --git a/reqs/setup.txt b/reqs/setup.txt index dd0bfbddc..3423c3ab9 100644 --- a/reqs/setup.txt +++ b/reqs/setup.txt @@ -1,4 +1,4 @@ -Babel +babel PySide6 setuptools wheel From 3929231fef46b067644da924a22dbb67bcd2b2fe Mon Sep 17 00:00:00 2001 From: Martin Koerner Date: Thu, 26 Feb 2026 20:32:08 +0100 Subject: [PATCH 7/8] Fix deprecation warnings --- osx/app_resources/plover_launcher.m | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/osx/app_resources/plover_launcher.m b/osx/app_resources/plover_launcher.m index 75e3a2ddc..eba4f9edd 100644 --- a/osx/app_resources/plover_launcher.m +++ b/osx/app_resources/plover_launcher.m @@ -28,13 +28,19 @@ int main(int argc, char *argv[]) { fprintf(stderr, "Fatal error: unable to decode python_home\n"); return 1; } - Py_SetPythonHome(python_home_w); // Set program name wchar_t* program = Py_DecodeLocale(argv[0], NULL); - Py_SetProgramName(program); - Py_Initialize(); + PyConfig config; + PyConfig_InitPythonConfig(&config); + PyConfig_SetString(&config, &config.home, python_home_w); + PyConfig_SetString(&config, &config.program_name, program); + PyConfig_SetBytesArgv(&config, argc, argv); // This automatically populates sys.argv + + Py_InitializeFromConfig(&config); + PyConfig_Clear(&config); + // ------------------------------ // After this point, we are in a Python interpreter. @@ -54,21 +60,10 @@ int main(int argc, char *argv[]) { if (pModule != NULL) { PyObject* pFunc = PyObject_GetAttrString(pModule, "main"); if (pFunc && PyCallable_Check(pFunc)) { - // Prepare arguments for main() - wchar_t** py_argv = (wchar_t**)PyMem_Malloc(sizeof(wchar_t*) * argc); - for (int i = 0; i < argc; i++) { - py_argv[i] = Py_DecodeLocale(argv[i], NULL); - } - PySys_SetArgv(argc, py_argv); - - // Call main() + + // Call main() - argv is already set in sys.argv! PyObject* pResult = PyObject_CallObject(pFunc, NULL); - for (int i = 0; i < argc; i++) { - PyMem_RawFree(py_argv[i]); - } - PyMem_RawFree(py_argv); - if (pResult == NULL) { PyErr_Print(); fprintf(stderr, "Call to main failed.\n"); From 8dd57848fe82f903c58414261601a35f48394ab4 Mon Sep 17 00:00:00 2001 From: Martin Koerner Date: Thu, 26 Feb 2026 20:58:45 +0100 Subject: [PATCH 8/8] Add news entry --- news.d/bugfix/1822.osx.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 news.d/bugfix/1822.osx.md diff --git a/news.d/bugfix/1822.osx.md b/news.d/bugfix/1822.osx.md new file mode 100644 index 000000000..4c3742d3b --- /dev/null +++ b/news.d/bugfix/1822.osx.md @@ -0,0 +1 @@ +Fix missing menu bar icon on macOS 26.