diff --git a/build/windows/installer/gimp3264.iss b/build/windows/installer/gimp3264.iss index ebeaf320fe..7b90639c3d 100755 --- a/build/windows/installer/gimp3264.iss +++ b/build/windows/installer/gimp3264.iss @@ -25,7 +25,7 @@ ;Install script for GIMP and GTK+ ;requires Inno Setup 6 ; -;See directories.isi +;See directories.isi ; ;Changelog: ; @@ -751,7 +751,7 @@ begin begin DebugMsg('DoConfigOverride', 'First call'); - + Result := False; ConfigOverride := coDontOverride; @@ -819,7 +819,7 @@ begin '/usr/bin/python=' + ExpandConstant('{app}\bin\{#PYTHON}') + #10 + '/usr/bin/python3=' + ExpandConstant('{app}\bin\{#PYTHON}') + #10 + ':Python:E::py::python:'#10; - + if not SaveStringToUTF8File(InterpFile,InterpContent,False) then begin DebugMsg('PrepareInterp','Problem writing the file. [' + InterpContent + ']'); @@ -840,7 +840,7 @@ begin '/usr/bin/luajit=' + ExpandConstant('{app}\bin\luajit.exe') + #10 + '/usr/bin/lua=' + ExpandConstant('{app}\bin\luajit.exe') + #10 + ':Lua:E::lua::luajit:'#10; - + if not SaveStringToUTF8File(InterpFile,InterpContent,False) then begin DebugMsg('PrepareInterp','Problem writing the file. [' + InterpContent + ']'); @@ -848,13 +848,32 @@ begin end; end; #endif + +// not optional +// !!! use comma for binfmt delimiter and full Windows path in interpreter field of binfmt +begin + InterpFile := ExpandConstant('{app}\lib\gimp\{#DIR_VER}\interpreters\gimp-script-fu-interpreter.interp'); + DebugMsg('PrepareInterp','Writing interpreter file for gimp-script-fu-interpreter: ' + InterpFile); + + InterpContent := 'gimp-script-fu-interpreter=' + ExpandConstant('{app}\bin\gimp-script-fu-interpreter-3.0.exe') + #10 + + 'gimp-script-fu-interpreter-3.0=' + ExpandConstant('{app}\bin\gimp-script-fu-interpreter-3.0.exe') + #10 + + '/usr/bin/gimp-script-fu-interpreter=' + ExpandConstant('{app}\bin\gimp-script-fu-interpreter-3.0.exe') + #10 + + ',ScriptFu,E,,scm,,' + ExpandConstant('{app}\bin\gimp-script-fu-interpreter-3.0.exe') + ','#10; + + if not SaveStringToUTF8File(InterpFile,InterpContent,False) then + begin + DebugMsg('PrepareInterp','Problem writing the file. [' + InterpContent + ']'); + SuppressibleMsgBox(CustomMessage('ErrorUpdatingScriptFu') + ' (2)',mbInformation,mb_ok,IDOK); + end; +end; + end; procedure PrepareGimpEnvironment(); var EnvFile,Env: String; begin - + StatusLabel(CustomMessage('SettingUpEnvironment'),''); //set PATH to be used by plug-ins @@ -862,7 +881,7 @@ begin DebugMsg('PrepareGimpEnvironment','Setting environment in ' + EnvFile); Env := #10'PATH=${gimp_installation_dir}\bin'; - + if IsComponentSelected('gimp32on64') then begin @@ -941,7 +960,7 @@ procedure CustomizeOnClick(Sender: TObject); begin DebugMsg('Install mode','Custom'); InstallMode := imCustom; - + CleanUpCustomWelcome(); WizardForm.NextButton.OnClick(TNewButton(Sender).Parent); @@ -1044,9 +1063,9 @@ begin else exit; DebugMsg('SelectComponentsFaceLift','2'); - + WizardForm.ComponentsList.OnClick := @ComponentsListOnClick; - + lblDescription := TNewStaticText.Create(WizardForm.ComponentsList.Parent) with lblDescription do begin @@ -1055,7 +1074,7 @@ begin AutoSize := True; Caption := CustomMessage('ComponentsDescription'); end; - + pnlDescription := TPanel.Create(WizardForm.ComponentsList.Parent); with pnlDescription do begin @@ -1069,7 +1088,7 @@ begin end; lblDescription.Parent := WizardForm.ComponentsList.Parent; //place lblDescription above pnlDescription - + lblComponentDescription := TNewStaticText.Create(pnlDescription); with lblComponentDescription do begin @@ -1081,7 +1100,7 @@ begin Height := Parent.Height - ScaleY(20); Top := ScaleY(12); end; - + end; @@ -1090,7 +1109,7 @@ var rtfNewReadyMemo: TRichEditViewer; begin DebugMsg('ReadyFaceLift',''); WizardForm.ReadyMemo.Visible := False; - + rtfNewReadyMemo := TRichEditViewer.Create(WizardForm.ReadyMemo.Parent); with rtfNewReadyMemo do begin @@ -1214,7 +1233,7 @@ begin Components := ['Gimp','Deps','Debug','Translations','MyPaint','Python','Ghostscript','Lua','Gimp32']; ComponentDesc := ''; - + for i := 0 to TNewCheckListBox(pSender).Items.Count - 1 do if TNewCheckListBox(pSender).Selected[i] then begin @@ -1277,7 +1296,7 @@ var sText: String; begin DebugMsg('UpdateReadyMemo',''); (* Prepare the text for new Ready Memo *) - + sText := RTFHeader; if pMemoDirInfo <> '' then sText := sText + ParseReadyMemoText(pSpace,pMemoDirInfo) + '\sb100'; @@ -1286,7 +1305,7 @@ begin If pMemoTasksInfo<>'' then sText := sText + '\sb100' + ParseReadyMemoText(pSpace,pMemoTasksInfo); - + ReadyMemoRichText := Copy(sText,1,Length(sText)-6) + '}'; Result := 'If you see this, something went wrong'; @@ -1299,7 +1318,7 @@ var NewBitmap1,NewBitmap2: TFileStream; begin WelcomeBitmapBottom := TBitmapImage.Create(WizardForm); with WelcomeBitmapBottom do - begin + begin Left := 0; Top := 0; Parent := WizardForm; @@ -1309,7 +1328,7 @@ begin end; DebugMsg('UpdateWizardImages','Height: ' + IntToStr(WizardForm.WizardBitmapImage.Height)); - + if WizardForm.WizardBitmapImage.Height < 386 then //use smaller image when not using Large Fonts begin try @@ -1375,7 +1394,7 @@ begin DebugMsg('DoUninstall','Install directory doesn'#39't exist: ' + InstallDir + ', resuming install'); oResult := InResult end else - begin + begin oResult := rogUninstallFailed; end; @@ -1472,7 +1491,7 @@ begin UninstallString := UninstallString + ' /NORESTART'; DoUninstall(UninstallString, OldPath, lblInfo2, Result); - + end; end; @@ -1509,7 +1528,7 @@ begin end; -procedure CurPageChanged(pCurPageID: Integer); +procedure CurPageChanged(pCurPageID: Integer); begin DebugMsg('CurPageChanged','ID: '+IntToStr(pCurPageID)); case pCurPageID of @@ -1678,15 +1697,15 @@ begin SetArrayLength(Buttons,2); Message[0] := CustomMessage('Require32BPP'); Buttons[0] := CustomMessage('Require32BPPContinue'); - Buttons[1] := CustomMessage('Require32BPPExit'); - if (not WizardSilent) and + Buttons[1] := CustomMessage('Require32BPPExit'); + if (not WizardSilent) and (MessageWithURL(Message, CustomMessage('Require32BPPTitle'), Buttons, mbError, 2, 2) = 2) then Result := False else Result := True; end else - Result := True; + Result := True; end; @@ -1733,7 +1752,7 @@ begin Result := RestartSetupAfterReboot(); //resume install after reboot - skip all setting pages, and install directly if Result then - Result := BPPTooLowWarning(); + Result := BPPTooLowWarning(); if not Result then //no need to do anything else exit; @@ -1743,7 +1762,7 @@ begin SetArrayLength(Buttons,2); Buttons[0] := CustomMessage('DevelopmentButtonContinue'); Buttons[1] := CustomMessage('DevelopmentButtonExit'); - if (not WizardSilent) and + if (not WizardSilent) and (MessageWithURL(Message, CustomMessage('DevelopmentWarningTitle'), Buttons, mbError, 2, 2) = 2) then begin Result := False; @@ -1810,4 +1829,3 @@ end; #include "uninst.isi" #expr SaveToFile(AddBackslash(SourcePath) + "Preprocessed.iss") - diff --git a/configure.ac b/configure.ac index 00e1f1e612..e370c1319c 100644 --- a/configure.ac +++ b/configure.ac @@ -3106,6 +3106,7 @@ plug-ins/script-fu/libscriptfu/Makefile plug-ins/script-fu/libscriptfu/ftx/Makefile plug-ins/script-fu/libscriptfu/tinyscheme/Makefile plug-ins/script-fu/server/Makefile +plug-ins/script-fu/interpreter/Makefile plug-ins/script-fu/scripts/Makefile plug-ins/script-fu/scripts/images/Makefile plug-ins/selection-to-path/Makefile diff --git a/plug-ins/script-fu/Makefile.am b/plug-ins/script-fu/Makefile.am index cb6def24e5..af6d5c8632 100644 --- a/plug-ins/script-fu/Makefile.am +++ b/plug-ins/script-fu/Makefile.am @@ -49,7 +49,7 @@ AM_LDFLAGS = \ $(framework_cocoa) \ $(xnone) -SUBDIRS = libscriptfu scripts server +SUBDIRS = libscriptfu scripts server interpreter # Each plugin must be installed in a dir of the same name as the executable script_fudir = $(gimpplugindir)/plug-ins/script-fu diff --git a/plug-ins/script-fu/interpreter/.gitignore b/plug-ins/script-fu/interpreter/.gitignore new file mode 100644 index 0000000000..361a0bb007 --- /dev/null +++ b/plug-ins/script-fu/interpreter/.gitignore @@ -0,0 +1,6 @@ +/Makefile.in +/Makefile +/.deps +/_libs +/.libs +/script-fu-interpreter.exe diff --git a/plug-ins/script-fu/interpreter/Makefile.am b/plug-ins/script-fu/interpreter/Makefile.am new file mode 100644 index 0000000000..d957c3acf8 --- /dev/null +++ b/plug-ins/script-fu/interpreter/Makefile.am @@ -0,0 +1,102 @@ +## Process this file with automake to produce Makefile.in + +# derived from app/Makefile.am, another installed binary + +if PLATFORM_OSX +xobjective_c = "-xobjective-c" +xobjective_cxx = "-xobjective-c++" +xnone = "-xnone" +framework_cocoa = -framework Cocoa +endif + +if OS_WIN32 +win32_ldflags = -mwindows -Wl,--tsaware $(WIN32_LARGE_ADDRESS_AWARE) + +if HAVE_EXCHNDL +exchndl = -lexchndl +endif + +else +libm = -lm +endif + +if ENABLE_RELOCATABLE_RESOURCES +munix = -Wl,-rpath '-Wl,$$ORIGIN/../lib' +endif + +if HAVE_WINDRES +include $(top_srcdir)/build/windows/gimprc-plug-ins.rule +script_fu_interpreter_RC = script-fu-interpreter.rc.o +endif + +libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la +libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la +libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la +libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la +libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la +libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la +libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la $(libm) + +# link to libscriptfu +libgimp_scriptfu = $(top_builddir)/plug-ins/script-fu/libscriptfu/libgimp-scriptfu-$(GIMP_API_VERSION).la + +# include srcdir parent to find libscriptfu include files +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(GTK_CFLAGS) \ + $(GEGL_CFLAGS) \ + -I$(includedir) \ + -I$(srcdir)/.. \ + -DG_LOG_DOMAIN=\"scriptfu\" + +AM_CFLAGS = \ + $(xobjective_c) + +AM_CXXFLAGS = \ + $(xobjective_cxx) + +AM_LDFLAGS = \ + $(munix) \ + $(win32_ldflags) \ + $(framework_cocoa) \ + $(xnone) + +# interpreter is-a plugin but is-a bin_PROGRAM +# i.e. installs to usual place of executables e.g. /usr/bin so shebangs find it. +bin_PROGRAMS = gimp-script-fu-interpreter-@GIMP_API_VERSION@ + +gimp_script_fu_interpreter_@GIMP_API_VERSION@_SOURCES = \ + script-fu-interpreter.c \ + script-fu-interpreter.h \ + script-fu-interpreter-plugin.c + +# link with libgimp-scriptfu +# link with the usual gimp suspects + +gimp_script_fu_interpreter_@GIMP_API_VERSION@_LDADD = \ + $(libgimp_scriptfu) \ + $(libgimpmath) \ + $(libgimp) \ + $(libgimpbase) \ + $(libgimpui) \ + $(libgimpwidgets) \ + $(libgimpconfig) \ + $(libgimpcolor) \ + $(GTK_LIBS) \ + $(GTK_MAC_INTEGRATION_LIBS) \ + $(RT_LIBS) \ + $(INTLLIBS) \ + $(exchndl) \ + $(script_fu_interpreter_RC) + +install-exec-hook: +if DEFAULT_BINARY + cd $(DESTDIR)$(bindir) \ + && rm -f gimp-script-fu-interpreter$(EXEEXT) \ + && $(LN_S) gimp-script-fu-interpreter-$(GIMP_APP_VERSION)$(EXEEXT) gimp-script-fu-interpreter$(EXEEXT) +endif + +uninstall-local: +if DEFAULT_BINARY + rm -f $(DESTDIR)$(bindir)/gimp-script-fu-interpreter$(EXEEXT) +endif diff --git a/plug-ins/script-fu/interpreter/README b/plug-ins/script-fu/interpreter/README new file mode 100644 index 0000000000..370077aa4f --- /dev/null +++ b/plug-ins/script-fu/interpreter/README @@ -0,0 +1,178 @@ +# About script-fu-interpreter + +It is GIMP's Scheme interpreter akin to any other language interpreter, +rather than a GIMP extension as is the plugin extension-script-fu. +This interpreter (like all the ScriptFu plugins) embeds a TinyScheme interpreter. + +It is an executable program that is passed the name of a .scm file. +The combination defines one or more PDB procedures of type PLUGIN. + +Differences from extension-script-fu +==================================== + +Since gimp-script-fu-interpreter and extension-script-fu use the same +embedded interpreter (they both wrap TinyScheme) +there is no difference in the language interpreted. +Any differences are in the larger behavior of plugins. + +### PDB Procedure type + +Defines PDB procedure(s) of type PLUGIN. +Unlike for the scriptfu extension, +where a .scm file defines PDB procedure(s) of type TEMPORARY +(owned by the PDB procedure of type PLUGIN named extension-script-fu) + +### Protocol + +Uses protocol to GIMP like other interpreters e.g. Python +(query, create, and run phases.) +The protocol to GIMP is unlike the protocol to the GIMP extension extension-script-fu. + +(Note that "extension" has many meanings. It can denote: a protocol, +or "suffix of a filename", or "a resource that extends GIMP." +extension-script-fu is "a resource that extends GIMP" and it also uses +the "extension" protocol, +while gimp-script-fu-interpreter is a "a resource that extends GIMP" but uses +the "plugin" protocol.) + +### Process lifetimes + +Executed many times, for many phases, +unlike extension-script-fu which stays executing and gets a remote procedure call +from GIMP to run a PDB procedure. + +### Process concurrency + +Each invocation of a plugin is in a separate process. +One plugin process crash does not affect others. +Unlike extension-script-fu, where a crash means the GIMP app must be restarted +to restart extension-script-fu. + +### GUI concurrency + +Each plugin can have its own GUI visible concurrently +with the GUI of other ScriptFu plugins. + +For extension-script-fu, an open dialog +prevents other plugins in /scripts (implemented by extension-script-fu) +from opening a dialog. +Instead extension-script-fu opens such dialogs sequentially. + +This difference is not very important, +since most users work sequentially. +Most dialogs for plugins do not do anything substantive +until a user closes the dialog with the OK button. + +### Calls between scripts + +In extension-script-fu, a call to another PDB procedure +implemented by TEMPORARY procedure owned by extension-script-fu +does not leave the process. + +In gimp-script-fu-interpreter, a call to another PDB procedure +implemented in another plugin file starts another process. + +For other plugins, most calls to another PDB procedure starts another process. +The exception is when one plugin file implements many PDB procedures. +One common case is when one plugin file implements its own TEMPORARY PDB procedures that +exist only for the duration of the plugin's lifetime. + +Naming +====== + +script-fu-interpreter is the informal name. + +Source is located in /plug-ins/script-fu/interpreter + +Filename of the executable is gimp-script-fu-interpreter-3.0. +The name is versioned by a number corresponding to the API +and the major version of GIMP (when script-fu-interpreter was introduced). +We expect plugin authors to be insulated from changes to script-fu-interpreter, +for the duration of the GIMP 3 version. + + +About .scm scripts for script-fu-interpreter +============================================ + +The contents of a .scm file queried by script-fu-interpreter +are the same as those handled by extension-script-fu +except for the addition of a shebang: + + #!/usr/bin/env gimp-script-fu-interpreter-3.0 + (define (script-fu-test img drawable) + ... + (script-fu-register "script-fu-test" + ... + +### Query of scripts + +As for other interpreters, a plugin script file must have certain attributes +to be queried by GIMP. But a queried file may define many PDB procedures. + +A .scm file queried by script-fu-interpreter: + +- must have permission to execute. +- must be in a directory names the same as the file's base name (less suffix.) + +A directory containing an .scm file queried by script-fu-interpreter +is usually a subdirectory of one of the /plug-ins directories, +unlike for extension-script-fu, where the .scm files are in a /scripts dir. + +A plugin directory should contain only one queriable .scm file: +only one file can have the same base name as the directory and +GIMP will query that file. + +### Defining many PDB procedures per directory + +When GIMP queries, script-fu-interpreter will load *ALL* the .scm files +in the same directory (regardless of shebang or execute permission.) +Any of the .scm files can declare and register a PDB procedure. +Any single .scm file can declare and register many PDB procedures. + +Similarly, when GIMP runs a named PDB procedure defined in an .scm file, +script-fu-interpreter will actually load *ALL* the .scm files +in the same directory, but run only the define run function so named. + +A plugin directory that contains many .scm files having a shebang +will also work, since only one can be named the same as the parent directory, +and GIMP will only query it, +but find the other .scm files with a shebang. + +### Requery of scripts + +As with other plugins, GIMP caches plugin definitions between sessions. +GIMP queries plugin files at startup. +GIMP will not requery any plugin files which have not changed since cached. +GIMP will not query a plugin file that is added to the file system +until GIMP is restarted. +(Filters>Development>ScriptFu>Refresh Scripts will not requery script files +handled by gimp-script-fu-interpreter, only those handled by extension-script-fu.) + +### Errors during query + +Most errors occurring during query appear only in the console. +If you are a plugin author, you should start GIMP in a console +so you can see such errors. + +A script may be malformed because it does not define +a "run" function having the same name as the PDB procedure name declared +in the call to script-fu-register(). +Formerly, such a malformed script was successfully queried but +the script would throw a "undefined variable" error at run time. +Now, such a malformed script is not queried successfully, +but throws a warning to the console: +"Run function not defined, or does not match PDB procedure name:" + + + +Test scripts +============ + +Small test scripts for gimp-script-fu-interpreter +are located in /plug-ins/script-fu/scripts/test + +The script ts-helloworld.scm, +formerly installed in /scripts and handled by extension-script-fu, +is now installed in /plug-ins/ts-helloworld/ts-helloworld.scm +and is now handled by gimp-script-fu-interpreter. +It appears as "Filters>Development>Script-Fu>Test>Hello World..." diff --git a/plug-ins/script-fu/interpreter/meson.build b/plug-ins/script-fu/interpreter/meson.build new file mode 100644 index 0000000000..b02523d104 --- /dev/null +++ b/plug-ins/script-fu/interpreter/meson.build @@ -0,0 +1,42 @@ + +scriptfuInclude = include_directories('..') + +executable_name = 'gimp-script-fu-interpreter-' + gimp_api_version + +plugin_sources = [ + 'script-fu-interpreter.c', + 'script-fu-interpreter-plugin.c', +] + +if platform_windows + plugin_sources += windows.compile_resources( + plugin_rc, + args: [ + '--define', 'ORIGINALFILENAME_STR="@0@"'.format(executable_name+'.exe'), + '--define', 'INTERNALNAME_STR="@0@"' .format(executable_name), + '--define', 'TOP_SRCDIR="@0@"' .format(meson.source_root()), + ], + include_directories: [ + rootInclude, appInclude, + ], + ) +endif + +# !!! Installs as a usual binary say to /usr/bin, unlike extension-script-fu +# GIMP queries scripts with shebangs, which invokes gimp-script-fu-interpreter-3.0. + +executable(executable_name, + plugin_sources, + dependencies: [ + libgimpui_dep, + math, + ], + c_args: [ + '-DG_LOG_DOMAIN="scriptfu"', + ], + include_directories: [ + scriptfuInclude, + ], + link_with : libscriptfu, + install: true, +) diff --git a/plug-ins/script-fu/interpreter/script-fu-interpreter-plugin.c b/plug-ins/script-fu/interpreter/script-fu-interpreter-plugin.c new file mode 100644 index 0000000000..1ea2492397 --- /dev/null +++ b/plug-ins/script-fu/interpreter/script-fu-interpreter-plugin.c @@ -0,0 +1,140 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file understands how to make a GimpPlugin of the interpreter. + * This is mostly boilerplate for any plugin. + * It understands little about ScriptFu internals, + * hidden by script-fu-interpreter.[ch] and libscriptfu. + */ + +#include "config.h" +#include +#include + +#include "libscriptfu/script-fu-intl.h" + +#include "script-fu-interpreter.h" + + +/* ScriptFuInterpreter subclasses GimpPlugIn */ + +#define SCRIPT_FU_INTERPRETER_TYPE (script_fu_interpreter_get_type ()) +G_DECLARE_FINAL_TYPE (ScriptFuInterpreter, script_fu_interpreter, SCRIPT, FU_INTERPRETER, GimpPlugIn) + +struct _ScriptFuInterpreter +{ + GimpPlugIn parent_instance; +}; + +static GList * script_fu_interpreter_query_procedures (GimpPlugIn *plug_in); +static GimpProcedure * script_fu_interpreter_create_procedure (GimpPlugIn *plug_in, + const gchar *name); + +G_DEFINE_TYPE (ScriptFuInterpreter, script_fu_interpreter, GIMP_TYPE_PLUG_IN) + +/* An alias to argv[1], which is the path to the .scm file. + * Each instance of ScriptFuInterpreter is specialized by the script passed in argv[1] + * This var need not belong to the class or to an instance, + * because there will only be one instance of ScriptFuInterpreter per plugin process. + */ +static gchar * path_to_this_script; + +/* Connect to Gimp. See libgimp/gimp.c. + * + * Can't use GIMP_MAIN macro, it doesn't omit argv[0]. + * + * First arg is app cited in the shebang. + * Second arg is the .scm file containing the shebang. + * Second to last arg is the "phase" e.g. -query or -run + * Last arg is the mode for crash dumps. + * Typical argv: + * gimp-script-fu-interpreter-3.0 ~/.config/GIMP/2.99/plug-ins/fu/fu + * -gimp 270 12 11 -query 1 + */ +int main (int argc, char *argv[]) +{ + g_debug ("Enter script-fu-interpreter main"); + + /* Alias path to this plugin's script file. */ + path_to_this_script = argv[1]; + + /* gimp_main will create an instance of the class given by the first arg, a GType. + * The class is a subclass of GimpPlugIn (with overridden query and create methods.) + * GIMP will subsequently callback the query or create methods, + * or the run_func of the PDB procedure of the plugin, + * depending on the "phase" arg in argv, + * which is set by the gimp plugin manager, which is invoking this interpreter. + */ + /* Omit argv[0] when passing to gimp */ + gimp_main (SCRIPT_FU_INTERPRETER_TYPE, argc-1, &argv[1] ); + + g_debug ("Exit script-fu-interpreter."); +} + +DEFINE_STD_SET_I18N + +static void +script_fu_interpreter_class_init (ScriptFuInterpreterClass *klass) +{ + GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass); + + plug_in_class->query_procedures = script_fu_interpreter_query_procedures; + plug_in_class->create_procedure = script_fu_interpreter_create_procedure; + plug_in_class->set_i18n = STD_SET_I18N; +} + + +/* called by the GType system to initialize instance of the class. */ +static void +script_fu_interpreter_init (ScriptFuInterpreter *script_fu) +{ + /* Nothing to do. */ +} + + +/* Return the names of PDB procedures implemented. A callback from GIMP. */ +static GList * +script_fu_interpreter_query_procedures (GimpPlugIn *plug_in) +{ + GList *result = NULL; + + g_debug ("queried"); + + result = script_fu_interpreter_list_defined_proc_names (plug_in, path_to_this_script); + if (g_list_length (result) < 1) + g_warning ("No procedures defined in %s", path_to_this_script); + + /* Caller is GIMP and it will free the list. */ + return result; +} + + +/* Create and return a GimpPDBProcedure, + * for the named one of the PDB procedures that the script implements. + * A callback from GIMP. + * + * Also set attributes on the procedure, most importantly, menu items (optional.) + * Also create any menus/submenus that the script defines e.g. Filters>My + */ +static GimpProcedure * +script_fu_interpreter_create_procedure (GimpPlugIn *plug_in, + const gchar *proc_name) +{ + return script_fu_interpreter_create_proc_at_path (plug_in, + proc_name, + path_to_this_script); +} diff --git a/plug-ins/script-fu/interpreter/script-fu-interpreter.c b/plug-ins/script-fu/interpreter/script-fu-interpreter.c new file mode 100644 index 0000000000..4113565d30 --- /dev/null +++ b/plug-ins/script-fu/interpreter/script-fu-interpreter.c @@ -0,0 +1,192 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" +#include +#include + +#include "libscriptfu/script-fu-lib.h" + +#include "script-fu-interpreter.h" + +/* Implementation of the outer ScriptFuInterpreter. + * This understands ScriptFu internals + * i.e. uses libscriptfu shared with other ScriptFu plugins e.g. extension-script-fu. + */ + +/* We don't need to load (into the interpreter) any .scm files handled by extension-script-fu. + * Namely, the .scm in the GIMP installation /scripts or in the user local /scripts dirs. + * + * During startup, GIMP might call gimp-script-fu-interpreter + * to query new files such as /plug-ins/fu/fu.scm. + * This is before extension-script-fu starts. + * But all the .scm files handled by extension-script-fu are type TEMPORARY and not needed + * for a /plug-ins/fu.scm to be queried. + * The only Scheme executed during query are calls to script-fu-register. + * Later, when a /plug-ins/fu.scm is run, it can call temporary PDB procedures + * that extension-script-fu provides. + * + * When we call script_fu_init_embedded_interpreter(), + * the passed paths should include the path to /scripts + * because that is the location of scripts for initialization and compatibility + * (script-fu.init, plug-in-compat.init and script-fu-compat.init, + * which are really scheme files.) + * + * scrip-fu-interpreter always inits embedded interpreter(allow_register=TRUE) + * In the "run" phase, you don't need script-fu-register to be defined, but its harmless. + */ + +static GFile *script_fu_get_plugin_parent_path (const gchar *path_to_this_script); +static void script_fu_free_path_list (GList **list); + + +/* Return a list of PDB procedure names defined in all .scm files in + * the parent dir of the given path, which is a filename of the one being queried. + * + * Each .scm file may contain many calls to script-fu-register, which defines a PDB procedure. + * All .scm files in the parent dir are searched. + * + * This executable is named script-fu-interpreter + * but no PDB procedure is named "script-fu-interpreter". + * Instead, the interpreter registers PDB procs named from name strings + * give in the in script-fu-register() calls in the interpreted scripts. + * + * Caller must free the list. + */ +GList * +script_fu_interpreter_list_defined_proc_names (GimpPlugIn *plug_in, + const gchar *path_to_this_script) +{ + GList *name_list = NULL; /* list of strings */ + GList *path_list = NULL; /* list of GFile */ + + /* path_list is /scripts dir etc. from which we will load compat and init scripts. + * second argument TRUE means define script-fu-register into the interpreter. + */ + path_list = script_fu_search_path (); + script_fu_init_embedded_interpreter (path_list, TRUE, GIMP_RUN_NONINTERACTIVE); + script_fu_free_path_list (&path_list); + + /* Reuse path_list, now a list of one path, the parent dir of the queried script. */ + path_list = g_list_append (path_list, + script_fu_get_plugin_parent_path (path_to_this_script)); + name_list = script_fu_find_scripts_list_proc_names (plug_in, path_list); + script_fu_free_path_list (&path_list); + + /* Usually name_list is not NULL i.e. not empty. + * But an .scm file that is not an actual GIMP plugin, or broken, may yield empty list. + */ + return name_list; +} + + +/* Create a PDB proc of type PLUGIN with the given name. + * Unlike extension-script-fu, create proc of type PLUGIN. + * + * We are in "create procedure" phase of call from GIMP. + * Create a PDB procedure that the script-fu-interpreter wraps. + * + * A GimpPDBProcedure has a run function, here script_fu_script_proc() + * of this outer interpreter. + * Sometime after the create, GIMP calls the run func, passing a name aka command. + * In ScriptFu, the same name is used for the PDB proc and the Scheme function + * which is the inner run func defined in the script. + * script_fu_script_proc calls the TinyScheme interpreter to evaluate + * the inner run func in the script. + */ +GimpProcedure * +script_fu_interpreter_create_proc_at_path (GimpPlugIn *plug_in, + const gchar *proc_name, + const gchar *path_to_this_script + ) +{ + GimpProcedure *procedure = NULL; + GList *path_list = NULL; /* list of GFile */ + + g_debug ("script_fu_interpreter_create_proc_at_path, name: %s", proc_name); + + /* Require proc_name is a suitable name for a PDB procedure eg "script-fu-test". + * (Not tested for canonical name "script-fu-") + * Require proc_name is a name that was queried earlier. + * Require the proc_name was defined in some .scm file + * in the same directory as the .scm file passed as argv[0]. + * The name of the .scm file eg "/plug-ins/fu/fu.scm" + * can be entirely different from proc_name. + * + * Otherwise, we simply won't find the proc_name defined in any .scm file, + * and will fail gracefully, returning NULL. + */ + + path_list = script_fu_search_path (); + path_list = g_list_append (path_list, + script_fu_get_plugin_parent_path (path_to_this_script)); + /* path_list are the /scripts dir, for .init and compat.scm, plus the path to this. + * second arg TRUE means define script-fu-register so it is effective. + */ + script_fu_init_embedded_interpreter (path_list, TRUE, GIMP_RUN_NONINTERACTIVE); + + /* Reuse path_list, now a list of only the path to this script. */ + script_fu_free_path_list (&path_list); + path_list = g_list_append (path_list, + script_fu_get_plugin_parent_path (path_to_this_script)); + + procedure = script_fu_find_scripts_create_PDB_proc_plugin (plug_in, path_list, proc_name); + script_fu_free_path_list (&path_list); + + /* When procedure is not NULL, assert: + * some .scm was evaluated. + * the script defined many PDB procedures locally, i.e. in script-tree + * we created a single PDB procedure (but not put it in the GIMP PDB) + * + * Ensure procedure is-a GimpProcedure or NULL. + * GIMP is the caller and will put non-NULL procedure in the PDB. + */ + return procedure; +} + + +/* Return GFile of the parent directory of this plugin, whose filename is given. + * + * Caller must free the GFile. + */ +static GFile * +script_fu_get_plugin_parent_path (const gchar *path_to_this_script) +{ + GFile *path = NULL; + GFile *parent_path = NULL; + + /* A libgimp GimpPlugin does not know its path, + * but its path was passed in argv to this interpreter. + * The path is to a file being queried e.g. "~/.config/GIMP/2.99/plug-ins/fu/fu.scm" + */ + g_debug ("path to this plugin %s", path_to_this_script); + path = g_file_new_for_path (path_to_this_script); + parent_path = g_file_get_parent (path); + g_object_unref (path); + return parent_path; +} + +/* Free a list of paths at the given handle. + * Ensures that the pointer to the list is NULL, prevents "dangling." + * g_list_free_full alone does not do that. + */ +static void +script_fu_free_path_list (GList **list) +{ + /* !!! g_steal_pointer takes a handle. */ + g_list_free_full (g_steal_pointer (list), g_object_unref); +} diff --git a/plug-ins/script-fu/interpreter/script-fu-interpreter.h b/plug-ins/script-fu/interpreter/script-fu-interpreter.h new file mode 100644 index 0000000000..ec48ce52fb --- /dev/null +++ b/plug-ins/script-fu/interpreter/script-fu-interpreter.h @@ -0,0 +1,29 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __SCRIPT_FU_INTERPRETER_H__ +#define __SCRIPT_FU_INTERPRETER_H__ + +GList *script_fu_interpreter_list_defined_proc_names ( + GimpPlugIn *plug_in, + const gchar *path_to_this_plugin); +GimpProcedure *script_fu_interpreter_create_proc_at_path ( + GimpPlugIn *plug_in, + const gchar *proc_name, + const gchar *path_to_this_script); + +#endif /* __SCRIPT_FU_INTERPRETER_H__ */ diff --git a/plug-ins/script-fu/libscriptfu/Makefile.am b/plug-ins/script-fu/libscriptfu/Makefile.am index 4f8c329a4f..3800578fe6 100644 --- a/plug-ins/script-fu/libscriptfu/Makefile.am +++ b/plug-ins/script-fu/libscriptfu/Makefile.am @@ -119,7 +119,9 @@ libgimp_scriptfu_@GIMP_API_VERSION@_la_SOURCES = \ scheme-wrapper.c \ scheme-wrapper.h \ script-fu-lib.c \ - script-fu-lib.h + script-fu-lib.h \ + script-fu-proc-factory.h \ + script-fu-proc-factory.c EXTRA_libgimp_scriptfu_@GIMP_API_VERSION@_la_DEPENDENCIES = $(scriptfu_def) diff --git a/plug-ins/script-fu/libscriptfu/meson.build b/plug-ins/script-fu/libscriptfu/meson.build index a7f1ecf03a..ffad8b51bf 100644 --- a/plug-ins/script-fu/libscriptfu/meson.build +++ b/plug-ins/script-fu/libscriptfu/meson.build @@ -14,6 +14,7 @@ libscriptfu_sources = [ 'script-fu-errors.c', 'script-fu-compat.c', 'script-fu-lib.c', + 'script-fu-proc-factory.c', ] # !! just "library(...)" which means shared versus static depends on configuration of project. diff --git a/plug-ins/script-fu/libscriptfu/script-fu-lib.c b/plug-ins/script-fu/libscriptfu/script-fu-lib.c index 93b988e256..235b2d35a7 100644 --- a/plug-ins/script-fu/libscriptfu/script-fu-lib.c +++ b/plug-ins/script-fu/libscriptfu/script-fu-lib.c @@ -19,7 +19,7 @@ #include "config.h" #include -/* FIXME script-fu-types.h refers to GtkAdjustment. */ +/* FIXME We only need gimpui because script-fu-types.h refers to GtkAdjustment. */ #include #include "script-fu-lib.h" @@ -28,6 +28,7 @@ #include "scheme-wrapper.h" /* tinyscheme_init etc, */ #include "script-fu-scripts.h" /* script_fu_find_scripts */ #include "script-fu-interface.h" /* script_fu_interface_is_active */ +#include "script-fu-proc-factory.h" /* @@ -67,13 +68,43 @@ script_fu_find_and_register_scripts ( GimpPlugIn *plugin, script_fu_find_scripts (plugin, paths); } +/* + * Init the embedded interpreter. + * + * allow_register: + * TRUE: allow loaded scripts to register PDB procedures. + * The scheme functions script-fu-register and script-fu-menu-register are + * defined to do something. + * FALSE: The scheme functions script-fu-register and script-fu-menu-register are + * defined but do nothing. + * + * Note that the embedded interpreter always defines scheme functions + * for all PDB procedures already existing when the interpreter starts + * (currently bound at startup, but its possible to lazy bind.) + * allow_register doesn't affect that. + */ void script_fu_init_embedded_interpreter ( GList *paths, gboolean allow_register, GimpRunMode run_mode) { + g_debug ("script_fu_init_embedded_interpreter"); tinyscheme_init (paths, allow_register); ts_set_run_mode (run_mode); + /* + * Ensure the embedded interpreter is running + * and has loaded its internal Scheme scripts + * and has defined existing PDB procs as Scheme foreign functions + * (is ready to interpret PDB-like function calls in scheme scripts.) + * + * scripts/...init and scripts/...compat.scm are loaded + * iff paths includes the "/scripts" dir. + * + * The .scm file(s) for plugins are loaded + * iff paths includes their parent directory (e.g. /scripts) + * Loaded does not imply yet registered in the PDB + * (yet, they soon might be for some phases of the plugin.) + */ } void @@ -155,7 +186,6 @@ script_fu_search_path (void) GList *path = NULL; path_str = gimp_gimprc_query ("script-fu-path"); - if (path_str) { GError *error = NULL; @@ -170,6 +200,23 @@ script_fu_search_path (void) g_clear_error (&error); } } - return path; } + + +GimpProcedure * +script_fu_find_scripts_create_PDB_proc_plugin (GimpPlugIn *plug_in, + GList *paths, + const gchar *name) +{ + /* Delegate to factory. */ + return script_fu_proc_factory_make_PLUGIN (plug_in, paths, name); +} + +GList * +script_fu_find_scripts_list_proc_names (GimpPlugIn *plug_in, + GList *paths) +{ + /* Delegate to factory. */ + return script_fu_proc_factory_list_names (plug_in, paths); +} diff --git a/plug-ins/script-fu/libscriptfu/script-fu-lib.h b/plug-ins/script-fu/libscriptfu/script-fu-lib.h index 09ac45a1b3..4b22445f12 100644 --- a/plug-ins/script-fu/libscriptfu/script-fu-lib.h +++ b/plug-ins/script-fu/libscriptfu/script-fu-lib.h @@ -42,4 +42,10 @@ void script_fu_run_read_eval_print_loop (void); void script_fu_register_quit_callback (void (*func) (void)); void script_fu_register_post_command_callback (void (*func) (void)); +GimpProcedure *script_fu_find_scripts_create_PDB_proc_plugin (GimpPlugIn *plug_in, + GList *paths, + const gchar *name); +GList *script_fu_find_scripts_list_proc_names (GimpPlugIn *plug_in, + GList *paths); + #endif /* __SCRIPT_FU_LIB_H__ */ diff --git a/plug-ins/script-fu/libscriptfu/script-fu-proc-factory.c b/plug-ins/script-fu/libscriptfu/script-fu-proc-factory.c new file mode 100644 index 0000000000..5c74a755ae --- /dev/null +++ b/plug-ins/script-fu/libscriptfu/script-fu-proc-factory.c @@ -0,0 +1,205 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" +#include +#include +#include + +#include "tinyscheme/scheme-private.h" +#include "script-fu-types.h" +#include "script-fu-scripts.h" +#include "script-fu-script.h" + +#include "script-fu-proc-factory.h" + +/* Local functions */ +static void script_fu_add_menu_to_procedure (GimpProcedure *procedure, + SFScript *script); + + +/* Methods to register PDB procs. A factory makes objects, here PDB procedures. + * + * Used by the outer script-fu-interpreter + * + * This is in libscriptfu to hide the SFScript type from outer plugins. + * These methods use instances of type SFScript as specs for procedures. + * + * FUTURE: migrate code. + * There are two flavors of factory-like code: for PDBProcType TEMPORARY and PLUGIN. + * extension-script-fu outer plugin only makes TEMPORARY + * script-fu-interpreter outer plugin only makes PLUGIN type + * This source file supports only script-fu-interpreter. + * script_fu_find_scripts() in script-fu-scripts.c is also a factory-like method, + * and could be extracted to a separate source file. + * Maybe more code sharing between the two flavors. + */ + + +/* Create and return a single PDB procedure of type PLUGIN, + * for the given proc name, by reading the script file in the given paths. + * Also add a menu for the procedure. + * + * PDB proc of type PLUGIN has permanent lifetime, unlike type TEMPORARY. + * + * The list of paths is usually just one directory, a subdir of /plug-ins. + * The directory may contain many .scm files. + * The plugin manager only queries one .scm file, + * having the same name as its parent dir and and having execute permission. + * But here we read all the .scm files in the directory. + * Each .scm file may register (and define run func for) many PDB procedures. + * + * Here, one name is passed, and though we load all the .scm files, + * we only create a PDB procedure for the passed name. + */ +GimpProcedure * +script_fu_proc_factory_make_PLUGIN (GimpPlugIn *plug_in, + GList *paths, + const gchar *proc_name) +{ + SFScript * script = NULL; + GimpProcedure * procedure = NULL; + + /* Reads all .scm files at paths, even though only one is pertinent. + * The returned script_tree is also in the state of the interpreter, + * we don't need the result here. + */ + (void) script_fu_find_scripts_into_tree (plug_in, paths); + + /* Get the pertinent script from the tree. */ + script = script_fu_find_script (proc_name); + + if (script) + { + procedure = script_fu_script_create_PDB_procedure ( + plug_in, + script, + script_fu_script_proc, /* run_func */ + GIMP_PDB_PROC_TYPE_PLUGIN); + script_fu_add_menu_to_procedure (procedure, script); + } + else + { + g_warning ("Failed to find script: %s.", proc_name); + } + return procedure; +} + + /* Traverse the list of scripts, for each defined name of a PDB proc, + * add it list whose handle is given. + * + * Order is not important. Could just as well prepend. + * + * This is a GTraverseFunction + */ +static gboolean +script_fu_append_script_names (gpointer *foo G_GNUC_UNUSED, + GList *scripts, + GList **name_list) +{ + for (GList * list = scripts; list; list = g_list_next (list)) + { + SFScript *script = list->data; + + if ( !script_fu_is_defined (script->name)) + { + g_warning ("Run function not defined, or does not match PDB procedure name: %s", + script->name); + continue; + } + + /* Must assign result from g_list_append back to name_list */ + *name_list = g_list_append ( (GList *) *name_list, g_strdup (script->name)); + } + return FALSE; /* We traversed all. */ + } + +/* Load script texts (.scm files) in the given paths. + * Iterate over all loaded scripts to get the PDB proc names they define. + * Return a list of the names. + */ +GList * +script_fu_proc_factory_list_names (GimpPlugIn *plug_in, + GList *paths) +{ + GList * result_list = NULL; + GTree * script_tree = NULL; + + /* Load (eval) all .scm files in all dirs in paths. */ + script_tree = script_fu_find_scripts_into_tree (plug_in, paths); + + /* Iterate over the tree, adding each script name to result list */ + g_tree_foreach (script_tree, + (GTraverseFunc) script_fu_append_script_names, + &result_list); + + return result_list; +} + +/* From scriptfu's internal data, add any menu to given procedure in the PDB. + * Requires that a script was just eval'ed so that scriptfu's list of menus + * declared in a script is valid. + * Requires the proc exists in PDB. + * + * Not ensure the PDB proc has a menu, when no menu was defined in the script. + * + * Derived from script_fu_install_menu, but that is specific to TEMPORARY procs. + * Also, unlike script_fu_install_menu, we don't nuke the menu list as we proceed. + * + * For each "create" of a procedure, the gimp-script-fu-interpreter is started anew, + * and a new script_menu_list is derived from the .scm file. + * We don't traverse the menu list more than once per session, which soon exits. + */ +static void +script_fu_add_menu_to_procedure (GimpProcedure *procedure, + SFScript *script) +{ + GList *menu_list; + gboolean did_add_menu = FALSE; + + menu_list = script_fu_get_menu_list (); + /* menu_list might be NULL: for loop will have no iterations. */ + + /* Each .scm file can declare many menu paths. + * Traverse the list to find the menu path defined for the procedure. + * Each SFMenu points to the procedure (SFScript) it belongs to. + */ + for (GList * traverser = menu_list; traverser; traverser = g_list_next (traverser)) + { + SFMenu *menu = traverser->data; + if (menu->script == script) + { + g_debug ("Add menu: %s", menu->menu_path); + gimp_procedure_add_menu_path (procedure, menu->menu_path); + did_add_menu = TRUE; + break; + } + } + + /* Some procedures don't have menu path. + * It is normal, but not common, to define procs of type PLUGIN that don't appear in the menus. + * No part of GIMP defaults a menu path for procedures. + * A menu label without a menu path is probably a mistake by the script author. + */ + if ( ! did_add_menu ) + { + /* Unusual for a .scm file to have no menu paths, but not an error. */ + g_debug ("No menu paths! Does the procedure name in script-fu-menu-register match?"); + /* FUTURE if the script defines a menu *label*, declare an error. */ + } + /* script_menu_list is a reference we do not need to free. */ + } diff --git a/plug-ins/script-fu/libscriptfu/script-fu-proc-factory.h b/plug-ins/script-fu/libscriptfu/script-fu-proc-factory.h new file mode 100644 index 0000000000..5daa7cc2b9 --- /dev/null +++ b/plug-ins/script-fu/libscriptfu/script-fu-proc-factory.h @@ -0,0 +1,27 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __SCRIPT_FU_PDB_PROC_FACTORY_H__ +#define __SCRIPT_FU_PDB_PROC_FACTORY_H__ + +GimpProcedure *script_fu_proc_factory_make_PLUGIN (GimpPlugIn *plug_in, + GList *paths, + const gchar *name); +GList *script_fu_proc_factory_list_names (GimpPlugIn *plug_in, + GList *paths); + +#endif /* __SCRIPT_FU_PDB_PROC_FACTORY__ */ diff --git a/plug-ins/script-fu/libscriptfu/script-fu-script.c b/plug-ins/script-fu/libscriptfu/script-fu-script.c index d9fa92eb6a..c1da3838e5 100644 --- a/plug-ins/script-fu/libscriptfu/script-fu-script.c +++ b/plug-ins/script-fu/libscriptfu/script-fu-script.c @@ -43,6 +43,8 @@ static gboolean script_fu_script_param_init (SFScript *script, gint n); + + /* * Function definitions */ @@ -165,26 +167,59 @@ script_fu_script_free (SFScript *script) g_slice_free (SFScript, script); } + +/* + * From the script, create a temporary PDB procedure, + * and install it as owned by the scriptfu extension PDB proc. + */ void script_fu_script_install_proc (GimpPlugIn *plug_in, SFScript *script, GimpRunFunc run_func) { GimpProcedure *procedure; - const gchar *menu_label = NULL; - gint arg_count[SF_DISPLAY] = { 0, }; - gint i; g_return_if_fail (GIMP_IS_PLUG_IN (plug_in)); g_return_if_fail (script != NULL); g_return_if_fail (run_func != NULL); + procedure = script_fu_script_create_PDB_procedure (plug_in, + script, + run_func, + GIMP_PDB_PROC_TYPE_TEMPORARY); + + gimp_plug_in_add_temp_procedure (plug_in, procedure); + g_object_unref (procedure); +} + + +/* + * Create and return a GimpProcedure. + * Caller typically either: + * install it owned by self as TEMPORARY type procedure + * OR return it as the result of a create_procedure callback from GIMP (PLUGIN type procedure.) + * + * Caller must unref the procedure. + */ +GimpProcedure * +script_fu_script_create_PDB_procedure (GimpPlugIn *plug_in, + SFScript *script, + GimpRunFunc run_func, + GimpPDBProcType plug_in_type) +{ + GimpProcedure *procedure; + const gchar *menu_label = NULL; + gint arg_count[SF_DISPLAY] = { 0, }; + gint i; + + g_debug ("script_fu_script_create_PDB_procedure: %s of type %i", script->name, plug_in_type); + /* Allow scripts with no menus */ if (strncmp (script->menu_label, "", 6) != 0) menu_label = script->menu_label; procedure = gimp_procedure_new (plug_in, script->name, - GIMP_PDB_PROC_TYPE_TEMPORARY, + plug_in_type, run_func, script, NULL); gimp_procedure_set_image_types (procedure, script->image_types); @@ -509,8 +544,7 @@ script_fu_script_install_proc (GimpPlugIn *plug_in, gimp_procedure_add_argument (procedure, pspec); } - gimp_plug_in_add_temp_procedure (plug_in, procedure); - g_object_unref (procedure); + return procedure; } void diff --git a/plug-ins/script-fu/libscriptfu/script-fu-script.h b/plug-ins/script-fu/libscriptfu/script-fu-script.h index c9a16f607a..a9901cd200 100644 --- a/plug-ins/script-fu/libscriptfu/script-fu-script.h +++ b/plug-ins/script-fu/libscriptfu/script-fu-script.h @@ -46,5 +46,11 @@ gchar * script_fu_script_get_command (SFScript *scrip gchar * script_fu_script_get_command_from_params (SFScript *script, const GimpValueArray *args); +GimpProcedure * script_fu_script_create_PDB_procedure (GimpPlugIn *plug_in, + SFScript *script, + GimpRunFunc run_func, + GimpPDBProcType plug_in_type); + + #endif /* __SCRIPT_FU_SCRIPT__ */ diff --git a/plug-ins/script-fu/libscriptfu/script-fu-scripts.c b/plug-ins/script-fu/libscriptfu/script-fu-scripts.c index eac9d0f49d..914ea1a6c6 100644 --- a/plug-ins/script-fu/libscriptfu/script-fu-scripts.c +++ b/plug-ins/script-fu/libscriptfu/script-fu-scripts.c @@ -43,13 +43,6 @@ #include "script-fu-intl.h" -typedef struct -{ - SFScript *script; - gchar *menu_path; -} SFMenu; - - /* * Local Functions */ @@ -65,11 +58,6 @@ static void script_fu_install_menu (SFMenu *menu); static gboolean script_fu_remove_script (gpointer foo, GList *scripts, gpointer data); -static GimpValueArray * script_fu_script_proc (GimpProcedure *procedure, - const GimpValueArray *args, - gpointer data); - -static SFScript * script_fu_find_script (const gchar *name); static gchar * script_fu_menu_map (const gchar *menu_path); static gint script_fu_menu_compare (gconstpointer a, @@ -88,13 +76,25 @@ static GList *script_menu_list = NULL; * Function definitions */ -void -script_fu_find_scripts (GimpPlugIn *plug_in, - GList *path) +/* Traverse list of paths, finding .scm files. + * Load and eval any found script texts. + * Script texts will call Scheme functions script-fu-register() + * and script-fu-menu-register(), + * which insert a SFScript record into script_tree, + * and insert a SFMenu record into script_menu_list. + * These are side effects on the state of the outer (SF) interpreter. + * + * Return the tree of scripts, as well as keeping a local pointer to the tree. + * The other result (script_menu_list) is not returned, see script_fu_get_menu_list(). + * + * Caller should free script_tree and script_menu_list, + * This should only be called once. + */ +GTree * +script_fu_find_scripts_into_tree ( GimpPlugIn *plug_in, + GList *paths) { - GList *list; - - /* Make sure to clear any existing scripts */ + /* Clear any existing scripts */ if (script_tree != NULL) { g_tree_foreach (script_tree, @@ -103,16 +103,46 @@ script_fu_find_scripts (GimpPlugIn *plug_in, g_tree_destroy (script_tree); } - if (! path) - return; - script_tree = g_tree_new ((GCompareFunc) g_utf8_collate); - for (list = path; list; list = g_list_next (list)) + if (paths) { - script_fu_load_directory (list->data); + GList *list; + + for (list = paths; list; list = g_list_next (list)) + { + script_fu_load_directory (list->data); + } } + /* + * Assert result is not NULL, but may be an empty tree. + * When paths is NULL, or no scripts found at paths. + */ + + g_debug ("script_fu_find_scripts_into_tree found %i scripts", g_tree_nnodes (script_tree)); + return script_tree; +} + +/* + * Return list of SFMenu for recently loaded scripts. + * List is non-empty only after a call to script_fu_find_scripts_into_tree(). + */ +GList * +script_fu_get_menu_list (void) +{ + return script_menu_list; +} + +/* Find scripts, create and install TEMPORARY PDB procedures, + * owned by self PDB procedure (e.g. extension-script-fu.) + */ +void +script_fu_find_scripts (GimpPlugIn *plug_in, + GList *path) +{ + script_fu_find_scripts_into_tree (plug_in, path); + /* Now that all scripts are read in and sorted, tell gimp about them */ g_tree_foreach (script_tree, (GTraverseFunc) script_fu_install_script, @@ -571,12 +601,13 @@ script_fu_run_command (const gchar *command, GString *output; gboolean success = FALSE; + g_debug ("script_fu_run_command: %s", command); output = g_string_new (NULL); ts_register_output_func (ts_gstring_output_func, output); if (ts_interpret_string (command)) { - g_set_error (error, 0, 0, "%s", output->str); + g_set_error (error, GIMP_PLUG_IN_ERROR, 0, "%s", output->str); } else { @@ -593,6 +624,8 @@ script_fu_load_directory (GFile *directory) { GFileEnumerator *enumerator; + g_debug ("Load dir: %s", g_file_get_parse_name (directory)); + enumerator = g_file_enumerate_children (directory, G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN "," @@ -665,8 +698,10 @@ script_fu_load_script (GFile *file) } } -/* - * The following function is a GTraverseFunction. +/* This is-a GTraverseFunction. + * + * Traverse. For each, install TEMPORARY PDB proc. + * Returning FALSE means entire list was traversed. */ static gboolean script_fu_install_script (gpointer foo G_GNUC_UNUSED, @@ -680,8 +715,12 @@ script_fu_install_script (gpointer foo G_GNUC_UNUSED, { SFScript *script = list->data; - script_fu_script_install_proc (plug_in, script, - script_fu_script_proc); + const gchar* name = script->name; + if (script_fu_is_defined (name)) + script_fu_script_install_proc (plug_in, script, + script_fu_script_proc); + else + g_warning ("Run function not defined, or does not match PDB procedure name: %s", name); } return FALSE; @@ -690,13 +729,14 @@ script_fu_install_script (gpointer foo G_GNUC_UNUSED, static void script_fu_install_menu (SFMenu *menu) { - GimpPlugIn *plug_in = gimp_get_plug_in (); - GimpProcedure *procedure; + GimpPlugIn *plug_in = gimp_get_plug_in (); + GimpProcedure *procedure = NULL; procedure = gimp_plug_in_get_temp_procedure (plug_in, menu->script->name); - gimp_procedure_add_menu_path (procedure, menu->menu_path); + if (procedure) + gimp_procedure_add_menu_path (procedure, menu->menu_path); g_free (menu->menu_path); g_slice_free (SFMenu, menu); @@ -726,7 +766,15 @@ script_fu_remove_script (gpointer foo G_GNUC_UNUSED, return FALSE; } -static GimpValueArray * +/* This is the outer "run func" for this plugin. + * When called, the name of the inner run func (code in Scheme language) + * is the first element of the value array. + * Form a command (text in Scheme language) that is a call to the the inner run func, + * evaluate it, and return the result, marshalled into a GimpValueArray. + * + * In the name 'script_fu_script_proc', 'proc' is a verb meaning 'process the script' + */ +GimpValueArray * script_fu_script_proc (GimpProcedure *procedure, const GimpValueArray *args, gpointer data) @@ -841,7 +889,7 @@ script_fu_lookup_script (gpointer *foo G_GNUC_UNUSED, return FALSE; } -static SFScript * +SFScript * script_fu_find_script (const gchar *name) { gconstpointer script = name; @@ -916,3 +964,43 @@ script_fu_menu_compare (gconstpointer a, return retval; } + +/* Is name a defined symbol in the interpreter state? + * (Defined in any script already loaded.) + * Where "symbol" has the usual lisp meaning: a unique name associated with + * a variable or function. + * + * The most common use is + * test the name of a PDB proc, which in ScriptFu must match + * a defined function that is the inner run function. + * I.E. check for typos by author of script. + * Used during query, to preflight so that we don't install a PDB proc + * that won't run later (during the run phase) + * giving "undefined symbol" for extension-script-fu. + * Note that if instead we create a PDB proc having no defined run func, + * script-fu-interpreter would load and define a same-named scheme function + * that calls the PDB, and can enter an infinite loop. + */ +gboolean +script_fu_is_defined (const gchar * name) +{ + gchar *scheme_text; + GError *error = NULL; + gboolean result; + + /* text to be interpreted is a call to an internal scheme function. */ + scheme_text = g_strdup_printf (" (symbol? %s ) ", name); + + /* Use script_fu_run_command, it correctly handles the string yielded. + * But we don't need the string yielded. + * If defined, string yielded is "#t", else is "Undefined symbol" or "#f" + */ + result = script_fu_run_command (scheme_text, &error); + if (!result) + { + g_debug ("script_fu_is_defined returns false"); + /* error contains string yielded by interpretation. */ + g_error_free (error); + } + return result; +} diff --git a/plug-ins/script-fu/libscriptfu/script-fu-scripts.h b/plug-ins/script-fu/libscriptfu/script-fu-scripts.h index b0079a39d3..b81e779ab1 100644 --- a/plug-ins/script-fu/libscriptfu/script-fu-scripts.h +++ b/plug-ins/script-fu/libscriptfu/script-fu-scripts.h @@ -26,5 +26,13 @@ pointer script_fu_add_script (scheme *sc, pointer script_fu_add_menu (scheme *sc, pointer a); +GTree * script_fu_find_scripts_into_tree (GimpPlugIn *plug_in, + GList *path); +SFScript * script_fu_find_script (const gchar *name); +GList * script_fu_get_menu_list (void); +GimpValueArray * script_fu_script_proc (GimpProcedure *procedure, + const GimpValueArray *args, + gpointer data); +gboolean script_fu_is_defined (const gchar *name); #endif /* __SCRIPT_FU_SCRIPTS__ */ diff --git a/plug-ins/script-fu/libscriptfu/script-fu-types.h b/plug-ins/script-fu/libscriptfu/script-fu-types.h index f0231b549e..981e450e0e 100644 --- a/plug-ins/script-fu/libscriptfu/script-fu-types.h +++ b/plug-ins/script-fu/libscriptfu/script-fu-types.h @@ -103,5 +103,10 @@ typedef struct SFArg *args; } SFScript; +typedef struct +{ + SFScript *script; // script which defined this menu path and label + gchar *menu_path; +} SFMenu; #endif /* __SCRIPT_FU_TYPES__ */ diff --git a/plug-ins/script-fu/libscriptfu/script-fu.def b/plug-ins/script-fu/libscriptfu/script-fu.def index 4d1f385df0..1c625bf088 100644 --- a/plug-ins/script-fu/libscriptfu/script-fu.def +++ b/plug-ins/script-fu/libscriptfu/script-fu.def @@ -13,3 +13,5 @@ EXPORTS script_fu_register_quit_callback script_fu_register_post_command_callback script_fu_search_path + script_fu_find_scripts_create_PDB_proc_plugin + script_fu_find_scripts_list_proc_names diff --git a/plug-ins/script-fu/meson.build b/plug-ins/script-fu/meson.build index c65cefcda7..c19d040f03 100644 --- a/plug-ins/script-fu/meson.build +++ b/plug-ins/script-fu/meson.build @@ -10,6 +10,7 @@ subdir('libscriptfu') subdir('scripts') subdir('server') +subdir('interpreter') executable_name = 'script-fu' diff --git a/plug-ins/script-fu/scripts/Makefile.am b/plug-ins/script-fu/scripts/Makefile.am index 75d89327a6..c9564d8f78 100644 --- a/plug-ins/script-fu/scripts/Makefile.am +++ b/plug-ins/script-fu/scripts/Makefile.am @@ -59,9 +59,17 @@ scripts = \ test_scripts = \ contactsheet.scm \ - test-sphere.scm \ + test-sphere.scm + +# scripts interpreted by gimp-script-fu-interpreter +# Each installs to a subdir of /plug-ins +# Each should have a shebang and execute permission +independent_scripts = \ ts-helloworld.scm +ts_helloworlddir = $(gimpplugindir)/plug-ins/ts-helloworld +ts_helloworld_SCRIPTS = ts-helloworld.scm + scriptdata_DATA = $(scripts) @@ -69,4 +77,4 @@ if GIMP_UNSTABLE scriptdata_DATA += $(test_scripts) endif -EXTRA_DIST = $(scripts) $(test_scripts) +EXTRA_DIST = $(scripts) $(test_scripts) $(independent_scripts) diff --git a/plug-ins/script-fu/scripts/meson.build b/plug-ins/script-fu/scripts/meson.build index 840f954240..5dc33b52b1 100644 --- a/plug-ins/script-fu/scripts/meson.build +++ b/plug-ins/script-fu/scripts/meson.build @@ -1,5 +1,6 @@ subdir('images') +# scripts interpreted by extension-script-fu, installed to /scripts scripts = [ 'add-bevel.scm', 'addborder.scm', @@ -58,7 +59,6 @@ if not stable scripts += [ 'contactsheet.scm', 'test-sphere.scm', - 'ts-helloworld.scm', ] endif @@ -66,3 +66,22 @@ install_data( scripts, install_dir: gimpdatadir / 'scripts', ) + +# scripts interpreted by gimp-script-fu-interpreter +# Each installed in subdirectory of /plug-in +# Each have a shebang and executable permission. +# Like other interpreted plugins. +# Lacking a shebang, a .interp file is needed to associate .scm suffix + +scripts_independent = [ + { 'name': 'ts-helloworld' }, +] + +foreach plugin : scripts_independent + name = plugin.get('name') + srcs = plugin.get('srcs', name + '.scm') + + install_data(srcs, + install_dir: gimpplugindir / 'plug-ins' / name, + install_mode: 'rwxr-xr-x') +endforeach diff --git a/plug-ins/script-fu/scripts/test/README b/plug-ins/script-fu/scripts/test/README new file mode 100644 index 0000000000..4e2f613210 --- /dev/null +++ b/plug-ins/script-fu/scripts/test/README @@ -0,0 +1,5 @@ +Scripts to test script-fu-interpreter. + +Not usually installed. + +To use, copy the test dirs into /plugins and ensure the .scm files have execute permission. diff --git a/plug-ins/script-fu/scripts/test/test0/test0.scm b/plug-ins/script-fu/scripts/test/test0/test0.scm new file mode 100644 index 0000000000..2680258873 --- /dev/null +++ b/plug-ins/script-fu/scripts/test/test0/test0.scm @@ -0,0 +1,31 @@ +#!/usr/bin/env gimp-script-fu-interpreter-3.0 + +; Basic test of a .scm file interpreted by script-fu-interpreter +; +; Setup: copy this file w/ executable permission, and its parent dir to /plug-ins +; Example: to ~/.gimp-2.99/plug-ins/test0/test0.scm +; (That is custom to one user.) + +; Expect "Test>Test SF interpreter 0" in the menus +; Expect when chosen, message on GIMP message bar. + +; Also, remove the execute permission. +; Then expect not appear in GIMP menus (not queried.) + +; Also, make the name different from its parent dir. +; Then expect not appear in GIMP menus (not queried.) + +(define (script-fu-test0) + (gimp-message "Hello script-fu-test0") +) + +(script-fu-register "script-fu-test0" + "Test SF interpreter 0" + _"Just gives a message from Gimp" + "lkk" + "lkk" + "2022" + "" ; all image types +) + +(script-fu-menu-register "script-fu-test0" "/Test") diff --git a/plug-ins/script-fu/scripts/test/test1/test1.scm b/plug-ins/script-fu/scripts/test/test1/test1.scm new file mode 100644 index 0000000000..04f27af67a --- /dev/null +++ b/plug-ins/script-fu/scripts/test/test1/test1.scm @@ -0,0 +1,43 @@ +#!/usr/bin/env gimp-script-fu-interpreter-3.0 + +; Basic test that a second .scm file is also queried. +; Expect "Test>Test SF interpreter 1" in the menus +; Expect when chosen, message on GIMP message bar. + +; Also tests that one .scm file can define two PDB procedures +; File is queried once, yielding two names. +; Two separate procedures created. + + +(define (script-fu-test1) + (gimp-message "Hello script-fu-test1") +) + +(script-fu-register "script-fu-test1" + "Test SF interpreter 01" + _"Just gives a message from Gimp" + "lkk" + "lkk" + "2022" + "" ; all image types +) + +(script-fu-menu-register "script-fu-test1" "/Test") + + + + +(define (script-fu-test2) + (gimp-message "Hello script-fu-test2") +) + +(script-fu-register "script-fu-test2" + "Test SF interpreter 02" + _"Just gives a message from Gimp" + "lkk" + "lkk" + "2022" + "" ; all image types +) + +(script-fu-menu-register "script-fu-test2" "/Test") diff --git a/plug-ins/script-fu/scripts/test/test1/test3.scm b/plug-ins/script-fu/scripts/test/test1/test3.scm new file mode 100644 index 0000000000..49a89a5447 --- /dev/null +++ b/plug-ins/script-fu/scripts/test/test1/test3.scm @@ -0,0 +1,31 @@ +; !!! No shebang here + +; Test a second .scm file in the same directory as a queried .scm. +; The second .scm file need not be executable. +; The second .scm file need not have a shebang. +; The gimp-script-fu-interpreter will nevertheless load the second .scm +; while it is querying the first, executable .scm in the dir. +; The plugin manager queries the first executable, +; and gimp-script-fu-interpreter loads (and returns defined names in) +; the second during the query of the first. + +; Expect "Test>Test SF interpreter 3" in the menus +; Expect when chosen, message on GIMP message bar. + +; plug-ins/test1/test1.scm is executable +; plug-ins/test1/test3.scm is NOT executable + +(define (script-fu-test3) + (gimp-message "Hello script-fu-test3") +) + +(script-fu-register "script-fu-test3" + "Test SF interpreter 3" + _"Just gives a message from Gimp" + "lkk" + "lkk" + "2022" + "" ; all image types +) + +(script-fu-menu-register "script-fu-test3" "/Test") diff --git a/plug-ins/script-fu/scripts/test/test4/test4.scm b/plug-ins/script-fu/scripts/test/test4/test4.scm new file mode 100644 index 0000000000..de515831dd --- /dev/null +++ b/plug-ins/script-fu/scripts/test/test4/test4.scm @@ -0,0 +1,25 @@ +#!/usr/bin/env gimp-script-fu-interpreter-3.0 + +; Test a .scm file that does not call script-fu-menu-register +; The menu will NOT default. +; Expect "Test SF interpreter 4" to NOT EXIST in any menu +; Expect the PDB proc "script-fu-test4" does appear in the PDB Brower + +; Two test cases: +; Alongside an executable script: plug-ins/test4/test4.scm NOT executable +; Executable, in its own directory: plug-ins/test1/test4.scm is executable + +(define (script-fu-test4) + (gimp-message "Hello script-fu-test4") +) + +(script-fu-register "script-fu-test4" + "Test SF interpreter 4" + _"Just gives a message from Gimp" + "lkk" + "lkk" + "2022" + "" ; all image types +) + +; !!! No call to script-fu-menu-register diff --git a/plug-ins/script-fu/scripts/test/test5/test5.scm b/plug-ins/script-fu/scripts/test/test5/test5.scm new file mode 100644 index 0000000000..79f3937886 --- /dev/null +++ b/plug-ins/script-fu/scripts/test/test5/test5.scm @@ -0,0 +1,16 @@ +#!/usr/bin/env gimp-script-fu-interpreter + +; Test a .scm file with an invalid shebang +; Note "-3.0" missing above. + +; The test depends on platform and env and .interp +; Must not be a file system link from gimp-script-fu-interpreter to gimp-script-fu-interpreter-3.0 +; Must not be a .interp file having "gimp-script-fu-interpreter=gimp-script-fu-interpreter-3.0" + +; Expect in the console: "/usr/bin/env: 'script-fu-interpreter': No such file or directory" + +(define (script-fu-test5) + (gimp-message "Hello script-fu-test5") +) + +; !!! No call to script-fu-menu-register diff --git a/plug-ins/script-fu/scripts/test/test6/test6.scm b/plug-ins/script-fu/scripts/test/test6/test6.scm new file mode 100644 index 0000000000..f5035f2a97 --- /dev/null +++ b/plug-ins/script-fu/scripts/test/test6/test6.scm @@ -0,0 +1,12 @@ +#!/usr/bin/env gimp-script-fu-interpreter-3.0 + +; Test a .scm file that does not register any procedure + +; Expect in the console: +; "(test6.scm:164): scriptfu-WARNING **: 10:06:07.966: No procedures defined in /work/.home/.config/GIMP/2.99/plug-ins/test6/test6.scm" + +(define (script-fu-test6) + (gimp-message "Hello script-fu-test6") +) + +; !!! No call to script-fu-register diff --git a/plug-ins/script-fu/scripts/test/test7/test7.scm b/plug-ins/script-fu/scripts/test/test7/test7.scm new file mode 100644 index 0000000000..913772eb43 --- /dev/null +++ b/plug-ins/script-fu/scripts/test/test7/test7.scm @@ -0,0 +1,28 @@ +#!/usr/bin/env gimp-script-fu-interpreter-3.0 + +; Test non-canonical name for PDB procedure +; gimp-script-fu-interpreter does not enforce canonical name. +; Other parts of GIMP (PDB) does not enforce canonical name +; for PDB procedures defined by .scm scripts. + +; Canonical means starts with "script-fu-" +; Here the name doesn't, its just "test7" + +; Expect "Test>Test SF interpreter 7" in the menus +; Expect when chosen, message on GIMP message bar. + + +(define (test7) + (gimp-message "Hello test7") +) + +(script-fu-register "test7" + "Test SF interpreter 7" + _"Just gives a message from Gimp" + "lkk" + "lkk" + "2022" + "" ; all image types +) + +(script-fu-menu-register "test7" "/Test") diff --git a/plug-ins/script-fu/scripts/test/test8/test8.scm b/plug-ins/script-fu/scripts/test/test8/test8.scm new file mode 100644 index 0000000000..f7cd41998b --- /dev/null +++ b/plug-ins/script-fu/scripts/test/test8/test8.scm @@ -0,0 +1,39 @@ +#!/usr/bin/env gimp-script-fu-interpreter-3.0 + +; Test mismatch between name of defined run function and name for PDB procedure +; Not a high priority: a rare syntax error in a plugin text. +; If authors follow a template, they won't make this mistake. + +; The names must match exactly. +; Here, "mismatch" the name of the defined run function +; does not match "script-fu-test8" the name of the PDB proc. + +; Expect a warning in the text console as the plugin text is queried: +; script-fu: WARNING: Run function not defined, or does not match PDB procedure name: script-fu-test8. +; Expect the PDB procedure to not exist + +; If we don't detect this syntax error: +; A PDB procedure is created. +; When invoked from Test>Test SF interpreter 8" +; the interpreter enters an infinite loop. +; There is no harm to the GIMP app, but the interpreter process can only be killed. +; During the run phase, the "(define foo)" +; should re-define an existing definition in the interpreter state. +; Instead, since the name is mismatched, +; the foo function remains defined to be a call to the PDB procedure named foo. +; So script-fu-script-proc instead calls the PDB again, an infinite loop. + +(define (mismatch) + (gimp-message "mismatch") +) + +(script-fu-register "script-fu-test8" + "Test SF interpreter 8" + _"Just gives a message from Gimp" + "lkk" + "lkk" + "2022" + "" ; all image types +) + +(script-fu-menu-register "script-fu-test8" "/Test") diff --git a/plug-ins/script-fu/scripts/ts-helloworld.scm b/plug-ins/script-fu/scripts/ts-helloworld.scm index 075c1ef1ed..9611d520e3 100644 --- a/plug-ins/script-fu/scripts/ts-helloworld.scm +++ b/plug-ins/script-fu/scripts/ts-helloworld.scm @@ -1,3 +1,5 @@ +#!/usr/bin/env gimp-script-fu-interpreter-3.0 + ; "Hello, World" Test v1.00 February 29, 2004 ; by Kevin Cozens ;