From b059896975c15fc5a6f91c700171295071bae9f5 Mon Sep 17 00:00:00 2001 From: Bruno Lopes Date: Sun, 9 Nov 2025 12:29:10 -0300 Subject: [PATCH] app, build, plug-ins: Run interpreters conditionally on Windows console Closes: #12642 Now, we will check at runtime if GIMP is not on a console and use the corresponding _win.interp file on such case only. (cherry picked from commit 0c70a9fe00b1f0a82b6d1312c7b5990db7f4c715) --- app/plug-in/gimpinterpreterdb.c | 65 ++++++++++++++++++++++- build/windows/2_bundle-gimp-uni_base.py | 7 ++- build/windows/installer/base_gimp3264.iss | 36 +++++++------ data/interpreters/meson.build | 1 + extensions/meson.build | 1 + plug-ins/python/meson.build | 16 +++--- plug-ins/python/pygimp_win.interp | 5 ++ plug-ins/script-fu/meson.build | 10 +++- 8 files changed, 111 insertions(+), 30 deletions(-) create mode 100644 plug-ins/python/pygimp_win.interp diff --git a/app/plug-in/gimpinterpreterdb.c b/app/plug-in/gimpinterpreterdb.c index 5a9202b58b..e4e34b0609 100644 --- a/app/plug-in/gimpinterpreterdb.c +++ b/app/plug-in/gimpinterpreterdb.c @@ -38,6 +38,10 @@ #include "libgimpbase/gimpbase.h" +#ifdef G_OS_WIN32 +#include +#endif + #include "plug-in-types.h" #include "gimpinterpreterdb.h" @@ -121,11 +125,44 @@ gimp_interpreter_db_new (gboolean verbose) return db; } +#ifdef G_OS_WIN32 +static gboolean +might_be_console_process (void) +{ + /* check for Unix console */ + if (g_getenv ("TERM") || g_getenv ("SHELL")) + return TRUE; + + /* check for Windows console (taken from gspawn-win32.c) */ + gboolean attached_to_self = AttachConsole (GetCurrentProcessId ()); + g_return_val_if_fail (! attached_to_self, TRUE); + + switch (GetLastError ()) + { + case ERROR_ACCESS_DENIED: + return TRUE; + case ERROR_INVALID_HANDLE: + return FALSE; + } + g_return_val_if_reached (FALSE); +} +#endif + void gimp_interpreter_db_load (GimpInterpreterDB *db, GList *path) { GList *list; +#ifdef G_OS_WIN32 + static gboolean console_checked = FALSE; + static gboolean is_console = FALSE; + + if (!console_checked) + { + is_console = might_be_console_process (); + console_checked = TRUE; + } +#endif g_return_if_fail (GIMP_IS_INTERPRETER_DB (db)); @@ -168,8 +205,32 @@ gimp_interpreter_db_load (GimpInterpreterDB *db, { GFile *file = g_file_enumerator_get_child (enumerator, info); - gimp_interpreter_db_load_interp_file (db, file); - + gchar *basename = g_file_get_basename (file); +#ifndef G_OS_WIN32 + /* Unix: always load regular .interp file */ + if (! g_strrstr (basename, "_win")) + { + gimp_interpreter_db_load_interp_file (db, file); + } +#else + if (! g_strrstr (basename, "_win")) + { + /* Windows: only load regular .interp file if on console */ + if (is_console) + { + gimp_interpreter_db_load_interp_file (db, file); + } + } + else + { + /* Windows: load special _win .interp file if not on console */ + if (! is_console) + { + gimp_interpreter_db_load_interp_file (db, file); + } + } +#endif + g_free (basename); g_object_unref (file); } diff --git a/build/windows/2_bundle-gimp-uni_base.py b/build/windows/2_bundle-gimp-uni_base.py index 111f6ac8fd..7c118ed7ba 100644 --- a/build/windows/2_bundle-gimp-uni_base.py +++ b/build/windows/2_bundle-gimp-uni_base.py @@ -158,11 +158,10 @@ bundle(GIMP_PREFIX, "lib/girepository-*/*.typelib") bundle(MSYSTEM_PREFIX, "lib/girepository-*/*.typelib") bundle(MSYSTEM_PREFIX, "bin/libgirepository-*.dll") #### Python support -#####python.exe is needed for plug-ins output in `gimp-console*.exe` +#####python.exe is needed for plug-ins error output if `gimp*.exe` is run from console bundle(MSYSTEM_PREFIX, "bin/python.exe") -if not os.getenv("GIMP_UNSTABLE") and os.getenv("GIMP_RELEASE"): - #####pythonw.exe is needed to run plug-ins silently in `gimp*.exe` - bundle(MSYSTEM_PREFIX, "bin/pythonw.exe") +#####pythonw.exe is needed to run plug-ins silently if `gimp*.exe` is run from shortcut +bundle(MSYSTEM_PREFIX, "bin/pythonw.exe") bundle(MSYSTEM_PREFIX, "lib/python*") clean(GIMP_DISTRIB, "lib/python*/*.pyc") #####Needed for internet connection on python. See: https://gitlab.gnome.org/GNOME/gimp/-/issues/14722 diff --git a/build/windows/installer/base_gimp3264.iss b/build/windows/installer/base_gimp3264.iss index bfa90c8f72..7bcb5cd42e 100644 --- a/build/windows/installer/base_gimp3264.iss +++ b/build/windows/installer/base_gimp3264.iss @@ -407,7 +407,7 @@ Source: "{#MAIN_BUNDLE}\*.lua"; DestDir: "{app}"; Excludes: "share\gimp\*.lua"; #ifdef PYTHON Source: "{#MAIN_BUNDLE}\etc\ssl\*"; DestDir: "{app}\etc\ssl"; Components: {#PY_ARCHS}; Flags: {#COMMON_FLAGS} Source: "{#MAIN_BUNDLE}\lib\gimp\{#GIMP_PKGCONFIG_VERSION}\environ\py*.env"; DestDir: "{app}\lib\gimp\{#GIMP_PKGCONFIG_VERSION}\environ"; Components: {#PY_ARCHS}; Flags: {#COMMON_FLAGS} -Source: "{#MAIN_BUNDLE}\lib\gimp\{#GIMP_PKGCONFIG_VERSION}\interpreters\pygimp.interp"; DestDir: "{app}\lib\gimp\{#GIMP_PKGCONFIG_VERSION}\interpreters"; Components: {#PY_ARCHS}; Flags: {#COMMON_FLAGS} +Source: "{#MAIN_BUNDLE}\lib\gimp\{#GIMP_PKGCONFIG_VERSION}\interpreters\pygimp*.interp"; DestDir: "{app}\lib\gimp\{#GIMP_PKGCONFIG_VERSION}\interpreters"; Components: {#PY_ARCHS}; Flags: {#COMMON_FLAGS} Source: "{#MAIN_BUNDLE}\*.py"; DestDir: "{app}"; Components: {#PY_ARCHS}; Flags: {#COMMON_FLAGS} #endif Source: "{#MAIN_BUNDLE}\share\locale\*"; DestDir: "{app}\share\locale"; Components: loc; Flags: dontcopy {#COMMON_FLAGS} @@ -523,6 +523,7 @@ Type: filesandordirs; Name: "{app}\include\gexiv2" Type: files; Name: "{app}\uninst\uninst.inf" Type: files; Name: "{app}\lib\gimp\{#GIMP_PKGCONFIG_VERSION}\interpreters\lua.interp" Type: files; Name: "{app}\lib\gimp\{#GIMP_PKGCONFIG_VERSION}\environ\pygimp.env" +Type: files; Name: "{app}\lib\gimp\{#GIMP_PKGCONFIG_VERSION}\environ\pygimp_win.env" ;4.3 KEYS TO BE REGISTERED @@ -1750,23 +1751,28 @@ begin begin StatusLabel(CustomMessage('SettingUpPyGimp'),''); + //python.exe is needed for plug-ins error output if `gimp*.exe` is run from console InterpFile := ExpandConstant('{app}\lib\gimp\{#GIMP_PKGCONFIG_VERSION}\interpreters\pygimp.interp'); - DebugMsg('PrepareInterp','Writing interpreter file for gimp-python: ' + InterpFile); - -#if Defined(GIMP_UNSTABLE) || !Defined(GIMP_RELEASE) - //python.exe is prefered in unstable versions because of error output - #define PYTHON="python.exe" -#else - //pythonw.exe is prefered in stable releases because it works silently - #define PYTHON="pythonw.exe" -#endif - - InterpContent := 'python=' + ExpandConstant('{app}\bin\{#PYTHON}') + #10 + - 'python3=' + ExpandConstant('{app}\bin\{#PYTHON}') + #10 + - '/usr/bin/python=' + ExpandConstant('{app}\bin\{#PYTHON}') + #10 + - '/usr/bin/python3=' + ExpandConstant('{app}\bin\{#PYTHON}') + #10 + + DebugMsg('PrepareInterp','Writing interpreter file for gimp-python: ' + InterpFile); + InterpContent := 'python=' + ExpandConstant('{app}\bin\python.exe') + #10 + + 'python3=' + ExpandConstant('{app}\bin\python.exe') + #10 + + '/usr/bin/python=' + ExpandConstant('{app}\bin\python.exe') + #10 + + '/usr/bin/python3=' + ExpandConstant('{app}\bin\python.exe') + #10 + ':Python:E::py::python:'#10; + if not SaveStringToUTF8File(InterpFile,InterpContent,False) then + begin + DebugMsg('PrepareInterp','Problem writing the file. [' + InterpContent + ']'); + SuppressibleMsgBox(CustomMessage('ErrorUpdatingPython') + ' (2)',mbInformation,mb_ok,IDOK); + end; + //pythonw.exe is needed to run plug-ins silently if `gimp*.exe` is run from shortcut + InterpFile := ExpandConstant('{app}\lib\gimp\{#GIMP_PKGCONFIG_VERSION}\interpreters\pygimp_win.interp'); + DebugMsg('PrepareInterp','Writing interpreter file for gimp-python: ' + InterpFile); + InterpContent := 'python=' + ExpandConstant('{app}\bin\pythonw.exe') + #10 + + 'python3=' + ExpandConstant('{app}\bin\pythonw.exe') + #10 + + '/usr/bin/python=' + ExpandConstant('{app}\bin\pythonw.exe') + #10 + + '/usr/bin/python3=' + ExpandConstant('{app}\bin\pythonw.exe') + #10 + + ':Python:E::py::python:'#10; if not SaveStringToUTF8File(InterpFile,InterpContent,False) then begin DebugMsg('PrepareInterp','Problem writing the file. [' + InterpContent + ']'); diff --git a/data/interpreters/meson.build b/data/interpreters/meson.build index b25636db33..2857fc6a6d 100644 --- a/data/interpreters/meson.build +++ b/data/interpreters/meson.build @@ -1,3 +1,4 @@ +#.interp file read by gimp_interpreter_db_load_interp_file in gimpinterpreterdb.c install_data('default.interp', install_dir: gimpplugindir / 'interpreters', ) diff --git a/extensions/meson.build b/extensions/meson.build index 23132c2df0..6b084a81cf 100644 --- a/extensions/meson.build +++ b/extensions/meson.build @@ -1,5 +1,6 @@ subdir('goat-exercises') +#.interp file read by gimp_interpreter_db_load_interp_file in gimpinterpreterdb.c if have_lua and not meson.is_cross_build() and is_variable('lua') and lua.found() and (platform_windows or not relocatable_bundle) lua_config = configuration_data() # For Windows, we set the binary name only. diff --git a/plug-ins/python/meson.build b/plug-ins/python/meson.build index a417bb890c..2a2c563604 100644 --- a/plug-ins/python/meson.build +++ b/plug-ins/python/meson.build @@ -37,17 +37,17 @@ foreach plugin : python_plugins endforeach endforeach -# Fallback fix to the problem of non-configured interpreters (needed by MSIX) +#.interp file read by gimp_interpreter_db_load_interp_file in gimpinterpreterdb.c if (platform_windows or platform_osx) and not meson.is_cross_build() and python.found() python_config = configuration_data() if platform_windows - if not stable or not release - #python.exe is prefered in unstable versions because of error output - python_config.set('PYTHON_EXE', 'python.exe') - else - #pythonw.exe is prefered in stable releases because it works silently - python_config.set('PYTHON_EXE', 'pythonw.exe') - endif + # python.exe is needed for plug-ins error output if `gimp*.exe` is run from console + python_config.set('PYTHON_EXE', 'python.exe') + + # pythonw.exe is needed to run plug-ins silently if `gimp*.exe` is run from shortcut + install_data('pygimp_win.interp', + install_dir: gimpplugindir / 'interpreters', + ) else # A 'python3' symlink is created on macOS. python_config.set('PYTHON_EXE', 'python3') diff --git a/plug-ins/python/pygimp_win.interp b/plug-ins/python/pygimp_win.interp new file mode 100644 index 0000000000..1bd0107144 --- /dev/null +++ b/plug-ins/python/pygimp_win.interp @@ -0,0 +1,5 @@ +python=pythonw.exe +python3=pythonw.exe +/usr/bin/python=pythonw.exe +/usr/bin/python3=pythonw.exe +:Python:E::py::python3: diff --git a/plug-ins/script-fu/meson.build b/plug-ins/script-fu/meson.build index df0d12d619..d37a4d3315 100644 --- a/plug-ins/script-fu/meson.build +++ b/plug-ins/script-fu/meson.build @@ -46,7 +46,7 @@ if not meson.is_cross_build() ], ) - # Fallback fix to the problem of non-configured interpreters + #.interp file read by gimp_interpreter_db_load_interp_file in gimpinterpreterdb.c scriptfu_config = configuration_data() scriptfu_config.set('SCRIPTFU_PATH', '') configure_file( @@ -56,6 +56,14 @@ if not meson.is_cross_build() install: true, install_dir: gimpplugindir / 'interpreters', ) + + configure_file( + input : 'gimp-script-fu-interpreter.interp.in', + output: 'gimp-script-fu-interpreter_win.interp', + configuration: scriptfu_config, + install: true, + install_dir: gimpplugindir / 'interpreters', + ) endif # Several components use Gtk