Fix #10044 more natural binding of PDB return values

Allow (script-fu-use-v3) in script, or in SF Console.

Definitive description is in script-fu/docs/using-v3-binding.md

Makes SF interpret v3 of SF dialect.
  - marshals single return value from PDB without wrapping in list
  - marshals boolean return value from PDB as #t #f instead of integers
  - marshals boolean to PDB from #t and #f or TRUE and FALSE
  - marshals void return from PDB as () instead of (#t), but that is moot.

The version of SF dialect is distinct from the version of the PDB API.

Dialect v3 is opt-in: the initial dialect of all SF tools remains v2.

Commit also allows #t,#f for defaults of SF-TOGGLE instead of TRUE, FALSE
but that is an independent enhancement (but closely related.)

Affects interpreter state of the current process.
Not an attribute per se of a plugin.
While in this state, a plugin should not call PDB procedures
which are themselves v2 script plugins,
or a few utility scripts in script-fu-util.scm,
but that is rarely needed.

Does not remove symbols TRUE and FALSE from dialect.

A script can also call (script-fu-use-v2) to revert.
That is also discouraged but useful e.g. for testing.
This commit is contained in:
bootchk 2023-12-04 11:23:01 -05:00
parent d4af93b256
commit 98bf051e7a
12 changed files with 635 additions and 62 deletions

View File

@ -0,0 +1,282 @@
# The version 3 dialect of the ScriptFu language
## About
This describes a new dialect of ScriptFu,
used when a script calls: (script-fu-use-v3).
The new dialect is more like Scheme and makes scripts shorter.
The dialect only affects calls to the PDB.
The audience is script authors and other developers.
This describes the new dialect and how to port old scripts to the new dialect.
## Quick Start
A script that calls:
```
(script-fu-use-v3)
```
binds to certain PDB calls differently:
1. PDB procedures that return single values return just that single value,
*not wrapped in a list.*
Formerly, every PDB call returned a list (possibly nesting more lists.)
2. You can use #t and #f as arguments to PDB calls taking a boolean.
3. PDB calls returning a boolean return #t or #f, not TRUE or FALSE (1 or 0.)
## Script-Fu Console
The Script-Fu Console always starts in the v2 dialect.
You can call *script-fu-use-v3* in the console.
Then, the console interprets the v3 dialect.
It continues to interpret the v3 dialect in that session,
until you call *script-fu-use-v2.*
## Where to call *script-fu-use-v3* in scripts
Call *script-fu-use-v3* early.
This sets the dialect version for the remaining interpretation of the script.
Call *script-fu-use-v3* at the beginning of the run function.
!!! Do not call *script-fu-use-v3* at the top level of a script.
This has no effect, since it is only executed in the query phase.
The interpreter starts at the run function during the run phase.
*The interpreter always starts interpreting each script in the v2 dialect.
This is true even for extension-script-fu, the long-running interpreter.*
There is no need to call *script-fu-use-v2* before returning from a script,
to ensure that the next script is interpreted in v2 dialect.
Example:
```
(define (script-fu-testv3 img drawables )
(script-fu-use-v3) ; <<<
(let* (
...
```
### Technically speaking
The dialect version has "execution scope" versus "lexical scope."
Setting the dialect version is effective even for
other functions defined in the same script but lexically
outside the function where the dialect is set.
## Don't call v2 scripts from v3 scripts
When using the v3 dialect,
you can't call plugin scripts or other library scripts that depend on the v2 dialect.
And vice versa.
(When a script calls a PDB procedure that is a script,
a new interpreter process is *NOT* started.)
For example, a new plugin script should not call the PDB procedure
script-fu-add-bevel because it is implemented in ScriptFu Scheme
and for example has:
```
(width (car (gimp-drawable-get-width pic-layer)))
```
which is v2 dialect and would throw an error such as:
```
Error: car requires a pair.
```
*It is rare that a script calls another plugin script.*
A script usually calls the PDB,
but rarely calls a plugin script of the PDB.
There are very few, obscure library scripts that call the PDB using v2 dialect.
These are in scripts/script-fu-util.scm.
## Pure Scheme is unaffected
The dialect only affects calls the to PDB.
This means you can usually call most library scripts when using v3,
since most library scripts are pure Scheme,
that is, with no calls to the GIMP PDB.
## TRUE and FALSE
TRUE and FALSE are still in v3 dialect and are still numbers 1 and 0.
But we recommend not using them.
You can still pass them as arguments to PDB calls taking a boolean,
and they are still converted to the C notion of boolean truth.
FALSE which is 0 is truthy in Scheme!!!
It converts to the C notion of false only in a call the the PDB.
In the ScriptFu Console:
```
>(equal? FALSE #t)
#t
```
TRUE and FALSE symbols may become obsolete in the future.
## Plans for the future
This dialect is shorter and more natural for Scheme programmers.
The long-term goal is for the v3 dialect to be the only dialect of ScriptFu.
For the short term, for backward compatibility,
the default dialect of ScriptFu is the v2 dialect.
You should write any new scripts in the v3 dialect,
and call *script-fu-use-v3*.
You should plan on porting existing scripts to the v3 dialect,
since eventually ScriptFu might not support v2 dialect.
## Example conversions from v2 to v3
### A call to a PDB procedure returning a single value
```
(set! (width (car (gimp-drawable-get-width pic-layer))))
```
*must* become
```
(set! (width (gimp-drawable-get-width pic-layer)))
```
The PDB call returns a single integer,
so no *car* is needed.
### A call to a PDB procedure returning boolean
```
(if (= (gimp-image-is-rgb image) TRUE) ...
```
*must* become:
```
(if (gimp-image-is-rgb image) ...
```
The PDB procedure returns a boolean,
which is bound to #t or #f,
the usual symbols for Scheme truth.
### A call to a PDB procedure taking a boolean
```
(gimp-context-set-antialias TRUE)
```
*should* become
```
(gimp-context-set-antialias #t)
```
This doesn't *require* conversion because TRUE is 1 and is truthy in Scheme.
!!! But FALSE is 0 and 0 is truthy in Scheme
```
(gimp-context-set-antialias FALSE)
```
This *should* be converted, for clarity, but doesn't *require* conversion.
For now, ScriptFu still binds Scheme 0 to C 0.
So it still does what the script intends.
### A call to a PDB procedure returning a list
```
(set! brushes (car (gimp-brushes-get-list)))
```
*must* become:
```
(set! brushes (gimp-brushes-get-list))
```
Formerly, the PDB procedure returned a list wrapped in a list,
i.e. ((...))
In the v3 dialect, it returns just a list, i.e. (...)
which is a single value, a single container.
### A call to a PDB procedure returning a list, getting the first element
```
(set! first-brush (caar (gimp-brushes-get-list)))
```
Gets the first brush in GIMP.
This *must* become:
```
(set! first-brush (car (gimp-brushes-get-list)))
```
The call to *caar* is consecutive calls to *car*,
and you must eliminate the first call to *car*.
## Knowing what constructs need conversion
You *should* (but are not required to)
eliminate all uses of symbols TRUE and FALSE from a script
using v3 dialect.
You should eliminate many, *but not all*, uses of the *car* function wrapping a call to the PDB,
where the PDB procedure returns a single value.
There are several hundred such PDB procedures in the PDB.
Examine the signature of a PDB procedure using the PDB Browser.
For example, gimp-brush-get-angle:
```
Return Values
angle gdouble ....
Additional Information
```
This returns a single value so no need to use car.
For example, gimp-brushes-get-list:
```
Return Values
brush-list GStrv
Additional Information
```
This also returns a single value.
The single value is a list i.e. container.
In the v2 dialect, this returned a list wrapped in a list,
for example (("foo" "bar")).
See "Example conversions from v2 to v3"
### Does not require changes to calls to PDB procedures returning void
You should not need to change scripts on calls to PDB procedures returning C void because a script should not be examining the result.
Some scripts might examine the result of a call to a void PDB procedure, thinking that (#t) is the success of the call,
but that is a misconception and should be fixed.
Calls to PDB procedures that return C void return (#t) in v2 and the empty list (), sometimes called nil, in v3 dialect.
## Details of the implementation
There are new functions in the dialect:
```
script-fu-use-v3
script-fu-use-v2
```
These functions have side-effects on the state of the interpreter.
They always return the empty list ().
The effect is immediate.
The interpreter interprets a dialect from then on,
until the script returns,
or until the script changes the dialect again.
A call to *script-fu-use-v3* sets a flag in the state of the interpreter.
When the flag is set, the interpreter binds arguments to PDB calls slightly differently, as described.
Binding of args to PDB calls is done at run time.
Similarly, a call to *script-fu-use-v2* clears the flag
and restores the interpreter state to binding using the v2 dialect.
When in the v3 state,
any PDB call constructs using v2 binding will yield errors,
and vice versa.
Note that the difference in interpretation is only in binding, but both to and from the PDB:
1. Single return values *from* the PDB are not wrapped in lists.
2. Boolean return values *from* the PDB are bound to #t and #f.
3. Boolean values *to* the PDB can be #t and #f.

View File

@ -21,6 +21,7 @@ libscriptfu_sources = [
'script-fu-dialog.c',
'script-fu-run-func.c',
'script-fu-command.c',
'script-fu-version.c',
'script-fu-widgets-custom.c',
'script-fu-color.c',
'script-fu-resource.c',

View File

@ -19,6 +19,7 @@
#include "libgimp/gimp.h"
#include "tinyscheme/scheme-private.h"
#include "script-fu-errors.h"
#include "script-fu-version.h"
#include "scheme-marshal.h"
#include "scheme-marshal-return.h"
@ -164,8 +165,11 @@ marshal_PDB_return (scheme *sc,
*
* Returns a scheme "pointer" type referencing the scheme return value.
*
* The return value is a list.
* FUTURE: value is either a single value or a list.
* The return value depends on the SF dialect in use (script-fu-use-v3)
* v2: return value is a list.
* v3: value is either a single value for PDB procs returning solitary value,
* or an empty list for void PDB procs,
* or a list for PDB procs returning many values.
*
* Same error return as marshal_returned_PDB_values.
*/
@ -193,22 +197,36 @@ marshal_PDB_return_by_arity (scheme *sc,
if (return_arity == 0)
{
/* PDB procedure returns void.
* Every scheme function must return a value.
* Return (#t)
* FUTURE: return just sc->T, no reason to wrap it.
* result = sc->T;
* But every scheme function must return a value.
* What we return is moot: a caller should not use result of a void PDB procedure.
* This result is NOT an error status.
*/
g_debug ("void PDB proc returns (#t)");
result = sc->vptr->cons (sc, sc->T, sc->NIL);
if (is_interpret_v3_dialect ())
{
/* Marshal to `() satisfying (null? ) predicate.
* Note is truthy in Scheme, satisfies (if )
*/
result = sc->NIL;
}
else
{
/* v2 void PDB proc return marshals to (#t) */
result = sc->vptr->cons (sc, sc->T, sc->NIL);
}
}
else if (return_arity == 1)
{
/* Unary result.
* Return a list wrapping the result.
* FUTURE: return just unwrapped result (which can itself be a list.)
* i.e. just call marshal_returned_PDB_value (singular)
*/
result = marshal_returned_PDB_values (sc, values, &marshalling_error);
if (is_interpret_v3_dialect ())
{
/* Marshal to single value not wrapped in list. */
/* The value is second in the GVA, beyond the PDB status value. */
result = marshal_returned_PDB_value (sc, gimp_value_array_index (values, 1), 2, &marshalling_error);
}
else
{
/* v2 marshal to list of many values. */
result = marshal_returned_PDB_values (sc, values, &marshalling_error);
}
if (marshalling_error != NULL)
{
/* Propagate error. */
@ -217,9 +235,7 @@ marshal_PDB_return_by_arity (scheme *sc,
}
else /* >1 */
{
/* Many result values.
* Return a list wrapping the results. Similar to Python tuple return.
*/
/* Marshal to a list wrapping the results. Similar to Python tuple return.*/
result = marshal_returned_PDB_values (sc, values, &marshalling_error);
if (marshalling_error != NULL)
{
@ -229,11 +245,8 @@ marshal_PDB_return_by_arity (scheme *sc,
}
g_assert ( (result == NULL && *error != NULL)
|| (result != NULL && *error == NULL));
/* result is: (#t) or sc->NIL i.e. empty list or a non-empty list. */
/* FUTURE result is: #t or an atom or a vector
* or empty list or a non-empty list.
* A non-empty list is either a single result that itself is a list
* or a list wrapping a multiple result.
/* result is Scheme pointer to a Scheme data structure
* that depends on the dialect being interpreted (script-fu-use-v3)
*/
return result;
}
@ -252,12 +265,8 @@ marshal_PDB_return_by_arity (scheme *sc,
* The list can be non-homogenous (elements of different scheme types.)
*
* The returned list may be empty or have only a single element.
* FUTURE:
* When a PDB procedure returns a single value (which can be a container)
* do not wrap it in a list.
* It will be an error to call this function
* for PDB procedures that return a single value or return void.
* IOW, for PDB procedures of return arity < 2.
* In particular, when v2 dialect is in use, and the called PDB procedure
* returns a solitary value.
*/
static pointer
marshal_returned_PDB_values (scheme *sc,
@ -343,8 +352,9 @@ marshal_returned_PDB_values (scheme *sc,
* Currently, does not return atoms of scheme type byte or char
* (no PDB procedure returns those types.)
*
* !!! Returns a scheme number (0 or 1) for C type boolean.
* FUTURE: return atoms #f and #t.
* !!! For C type boolean, returned scheme type depends on dialect version:
* - v2 returns a scheme integer (0 or 1)
* - v3 returns atom #f or #t.
*/
static pointer
marshal_returned_PDB_value (scheme *sc,
@ -438,7 +448,16 @@ marshal_returned_PDB_value (scheme *sc,
else if (G_VALUE_HOLDS_BOOLEAN (value))
{
gboolean v = g_value_get_boolean (value);
result = sc->vptr->mk_integer (sc, v);
if (is_interpret_v3_dialect ())
{
/* Marshal to Scheme #t and #f */
result = v ? sc->T : sc->F;
}
else
{
/* v2 marshal to integer 0 or 1, same as TRUE FALSE symbols. C idiom */
result = sc->vptr->mk_integer (sc, v);
}
}
else if (G_VALUE_HOLDS_STRING (value))
{

View File

@ -43,6 +43,7 @@
#include "script-fu-scripts.h"
#include "script-fu-errors.h"
#include "script-fu-compat.h"
#include "script-fu-version.h"
#include "scheme-wrapper.h"
#include "scheme-marshal.h"
@ -78,6 +79,10 @@ static pointer script_fu_register_call_filter (scheme *sc,
pointer a);
static pointer script_fu_menu_register_call (scheme *sc,
pointer a);
static pointer script_fu_use_v3_call (scheme *sc,
pointer a);
static pointer script_fu_use_v2_call (scheme *sc,
pointer a);
static pointer script_fu_quit_call (scheme *sc,
pointer a);
static pointer script_fu_nil_call (scheme *sc,
@ -553,6 +558,7 @@ ts_init_procedures (scheme *sc,
ts_define_procedure (sc, "load-extension", scm_load_ext);
#endif
/* Define special functions used in scripts. */
if (register_scripts)
{
ts_define_procedure (sc, "script-fu-register", script_fu_register_call);
@ -566,18 +572,25 @@ ts_define_procedure (sc, "load-extension", scm_load_ext);
ts_define_procedure (sc, "script-fu-menu-register", script_fu_nil_call);
}
ts_define_procedure (sc, "script-fu-use-v3", script_fu_use_v3_call);
ts_define_procedure (sc, "script-fu-use-v2", script_fu_use_v2_call);
ts_define_procedure (sc, "script-fu-quit", script_fu_quit_call);
/* Define wrapper functions, not used in scripts.
* FUTURE: eliminate all but one, deprecated and permissive is obsolete.
*/
ts_define_procedure (sc, "gimp-proc-db-call", script_fu_marshal_procedure_call_strict);
ts_define_procedure (sc, "-gimp-proc-db-call", script_fu_marshal_procedure_call_permissive);
ts_define_procedure (sc, "--gimp-proc-db-call", script_fu_marshal_procedure_call_deprecated);
/* Define each PDB procedure as a scheme func.
* Each call passes through one of the wrapper funcs.
*/
proc_list = gimp_pdb_query_procedures (gimp_get_pdb (),
".*", ".*", ".*", ".*",
".*", ".*", ".*", ".*");
num_procs = proc_list ? g_strv_length (proc_list) : 0;
/* Register each procedure as a scheme func */
for (i = 0; i < num_procs; i++)
{
gchar *buff;
@ -854,11 +867,34 @@ script_fu_marshal_procedure_call (scheme *sc,
}
else if (G_VALUE_HOLDS_BOOLEAN (&value))
{
if (! sc->vptr->is_number (sc->vptr->pair_car (a)))
return script_type_error (sc, "numeric", i, proc_name);
if (sc->vptr->is_number (sc->vptr->pair_car (a)))
{
/* Bind according to C idiom: 0 is false, other numeric values true.
* This is not strict Scheme: 0 is truthy in Scheme.
* This lets FALSE still work, where FALSE is a deprecated symbol for 0.
*/
g_value_set_boolean (&value,
sc->vptr->ivalue (sc->vptr->pair_car (a)));
}
else
g_value_set_boolean (&value,
sc->vptr->ivalue (sc->vptr->pair_car (a)));
{
if (is_interpret_v3_dialect ())
{
/* Use Scheme semantics: anything but #f is true.
* This allows Scheme expressions yielding any Scheme type.
*/
/* is_false is not exported from scheme.c (but should be.)
* This is the same code: compare Scheme pointers.
*/
gboolean truth_value = ! (sc->vptr->pair_car (a) == sc->F);
g_value_set_boolean (&value, truth_value);
}
else
{
/* v2 */
return script_type_error (sc, "numeric", i, proc_name);
}
}
}
else if (G_VALUE_HOLDS_STRING (&value))
{
@ -1509,6 +1545,22 @@ script_fu_menu_register_call (scheme *sc,
return script_fu_add_menu (sc, a);
}
static pointer
script_fu_use_v3_call (scheme *sc,
pointer a)
{
begin_interpret_v3_dialect ();
return sc->NIL;
}
static pointer
script_fu_use_v2_call (scheme *sc,
pointer a)
{
begin_interpret_v2_dialect ();
return sc->NIL;
}
static pointer
script_fu_quit_call (scheme *sc,
pointer a)

View File

@ -24,6 +24,7 @@
#include "script-fu-types.h"
#include "script-fu-arg.h"
#include "script-fu-utils.h"
#include "script-fu-version.h"
/*
@ -497,8 +498,12 @@ script_fu_arg_append_repr_from_gvalue (SFArg *arg,
break;
case SF_TOGGLE:
g_string_append_printf (result_string, (g_value_get_boolean (gvalue) ?
"TRUE" : "FALSE"));
if (is_interpret_v3_dialect ())
{
g_string_append (result_string, (g_value_get_boolean (gvalue) ? "#t" : "#f"));
}
else
g_string_append (result_string, (g_value_get_boolean (gvalue) ? "TRUE" : "FALSE"));
break;
case SF_VALUE:
@ -634,7 +639,10 @@ script_fu_arg_append_repr_from_self (SFArg *arg,
break;
case SF_TOGGLE:
g_string_append (result_string, arg_value->sfa_toggle ? "TRUE" : "FALSE");
if (is_interpret_v3_dialect ())
g_string_append (result_string, arg_value->sfa_toggle ? "#t" : "#f");
else
g_string_append (result_string, arg_value->sfa_toggle ? "TRUE" : "FALSE");
break;
case SF_VALUE:

View File

@ -194,11 +194,26 @@ script_fu_parse_default_spec (scheme *sc,
break;
case SF_TOGGLE:
if (!sc->vptr->is_integer (default_spec))
return registration_error (sc, "toggle default must be an integer value");
arg->default_value.sfa_toggle =
(sc->vptr->ivalue (default_spec)) ? TRUE : FALSE;
/* Accept scheme boolean or int.
* This does not vary by language version, and makes language v2 more lenient.
*/
/* Note storing internally as a C int, which the widget wants.
* Elsewhere we marshal back to a Scheme data.
*
* Note that is_false is not exported from scheme.c, we compare Scheme pointers.
*
* The default value is from evaluating a Scheme expression.
* More in keeping with Scheme, should convert any value other than #f to C truth.
* Instead, convert only literal #t to C truth.
*/
if (sc->vptr->is_integer (default_spec))
arg->default_value.sfa_toggle = (sc->vptr->ivalue (default_spec)) ? TRUE : FALSE;
else if (default_spec == sc->T)
arg->default_value.sfa_toggle = 1;
else if (default_spec == sc->F)
arg->default_value.sfa_toggle = 0;
else
return registration_error (sc, "toggle default must yield an integer, #t, or #f");
break;
case SF_VALUE:

View File

@ -30,6 +30,7 @@
#include "script-fu-script.h"
#include "script-fu-scripts.h" /* script_fu_find_script */
#include "script-fu-command.h"
#include "script-fu-version.h"
#include "script-fu-run-func.h"
@ -42,6 +43,11 @@
* These return the result of interpretation,
* in a GimpValueArray whose only element is a status.
* !!! ScriptFu does not let authors define procedures that return values.
*
* A prior script may have called (script-fu-use-v3) to opt in to interpret v3 dialect.
* When this is long-running extension-script-fu process,
* ensure initial dialect is v2, the default.
* FUTURE: default is v3 and script must opt in to v2 dialect.
*/
/* run_func for a GimpImageProcedure
@ -73,6 +79,8 @@ script_fu_run_image_procedure (GimpProcedure *procedure, /* GimpImageProc
ts_set_run_mode (run_mode);
begin_interpret_default_dialect ();
switch (run_mode)
{
case GIMP_RUN_INTERACTIVE:
@ -154,6 +162,8 @@ script_fu_run_procedure (GimpProcedure *procedure,
ts_set_run_mode (run_mode);
begin_interpret_default_dialect ();
switch (run_mode)
{
case GIMP_RUN_INTERACTIVE:

View File

@ -0,0 +1,72 @@
/* 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 <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <libgimp/gimp.h>
/* Flag indicating version 3 binding to the PDB.
* while marshalling return values from PDB:
* - not wrap solitary values in list
* - bind c boolean to scheme truth values
*
* Set by a script calling (script-fu-use-v3).
* Cleared by a script calling (script-fu-use-v2).
*
* Initial, default state of all ScriptFu tools is interpret v2 dialect.
*
* New-style scripts call (script-fu-use-v3) at top level.
* Ends with process termination of the interpreter.
*
* Old-style scripts call in run function, but this is discouraged.
* Ends after extension-script-fu finishes interpretation of current command.
*
* Affects interpretation for the duration of the current interpreter process.
* !!! All script interpreted subsequently in the current process,
* especially in called PDB procedures that are themselves scripts,
* must use v3 binding to PDB.
* Note most old script plugins in the PDB use v2 that is affected by this flag.
* Note a very few routines in script-fu-util.scm call the PDB using v2 binding.
* So a script should not call them while this flag is set.
*/
static gboolean language_version_is_3 = FALSE;
void
begin_interpret_v3_dialect (void)
{
language_version_is_3 = TRUE;
}
void
begin_interpret_v2_dialect (void)
{
language_version_is_3 = FALSE;
}
/* the default dialect is v2 */
void
begin_interpret_default_dialect (void)
{
/* the default dialect is v2 */
begin_interpret_v2_dialect ();
}
gboolean
is_interpret_v3_dialect (void)
{
return language_version_is_3;
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#ifndef __SCRIPT_FU_VERSION_H__
#define __SCRIPT_FU_VERSION_H__
void begin_interpret_v3_dialect (void);
void begin_interpret_v2_dialect (void);
void begin_interpret_default_dialect (void);
gboolean is_interpret_v3_dialect (void);
#endif /* __SCRIPT_FU_VERSION__ */

View File

@ -53,6 +53,7 @@ if not stable
scripts += [
'contactsheet.scm',
'test-sphere.scm',
'test-v3.scm',
]
endif

View File

@ -9,6 +9,11 @@
; See also test-sphere.scm, for GIMP 2, from which this is derived
; Diffs marked with ; v3 >>>
; Also modified to use script-fu-use-v3
; I.E. binding of boolean and binding of PDB returns is changed.
; TRUE => #t in many places
; (car (...)) => (...) in many places
; v3 >>> signature of GimpImageProcedure
; drawables is a vector
@ -37,12 +42,13 @@
unused-layer
unused-channel
unused-drawable)
(script-fu-use-v3)
(let* (
(width (* radius 3.75))
(height (* radius 2.5))
(img (car (gimp-image-new width height RGB)))
(drawable (car (gimp-layer-new img width height RGB-IMAGE
"Sphere Layer" 100 LAYER-MODE-NORMAL)))
(img (gimp-image-new width height RGB)) ; v3 >>> elide car
(drawable (gimp-layer-new img width height RGB-IMAGE
"Sphere Layer" 100 LAYER-MODE-NORMAL))
(radians (/ (* light *pi*) 180))
(cx (/ width 2))
(cy (/ height 2))
@ -73,7 +79,8 @@
(if (and
(or (and (>= light 45) (<= light 75))
(and (<= light 135) (>= light 105)))
(= shadow TRUE))
; v3 >>> conditional doesn't need (= shadow TRUE)
shadow )
(let ((shadow-w (* (* radius 2.5) (cos (+ *pi* radians))))
(shadow-h (* radius 0.5))
(shadow-x cx)
@ -82,21 +89,21 @@
(begin (set! shadow-x (+ cx shadow-w))
(set! shadow-w (- shadow-w))))
(gimp-context-set-feather TRUE)
(gimp-context-set-feather #t)
(gimp-context-set-feather-radius 7.5 7.5)
(gimp-image-select-ellipse img CHANNEL-OP-REPLACE shadow-x shadow-y shadow-w shadow-h)
(gimp-context-set-pattern pattern)
(gimp-drawable-edit-fill drawable FILL-PATTERN)))
(gimp-context-set-feather FALSE)
(gimp-context-set-feather #f) ; v3 >>> FALSE => #f
(gimp-image-select-ellipse img CHANNEL-OP-REPLACE (- cx radius) (- cy radius)
(* 2 radius) (* 2 radius))
(gimp-context-set-gradient-fg-bg-rgb)
(gimp-drawable-edit-gradient-fill drawable
GRADIENT-RADIAL offset
FALSE 1 1
TRUE
#f 1 1 ; v3 >>> and also supersampling enum starts at 1 now
#t
light-x light-y
light-end-x light-end-y)
@ -108,20 +115,21 @@
(gimp-context-set-gradient-reverse gradient-reverse)
(gimp-drawable-edit-gradient-fill drawable
GRADIENT-LINEAR offset
FALSE 1 1
TRUE
#f 1 1
#t
10 10
30 60)
(gimp-selection-none img)
(gimp-context-set-foreground '(0 0 0))
(gimp-floating-sel-anchor (car (gimp-text-font img drawable
x-position y-position
multi-text
0 TRUE
size
font)))
(gimp-floating-sel-anchor (gimp-text-font
img drawable
x-position y-position
multi-text
0 #t
size
font))
(if (= orientation 1)
(gimp-image-rotate img ROTATE-DEGREES90))
@ -147,7 +155,7 @@
SF-ONE-OR-MORE-DRAWABLE ; v3 >>> additional argument
SF-ADJUSTMENT "Radius (in pixels)" (list 100 1 5000 1 10 0 SF-SPINNER)
SF-ADJUSTMENT "Lighting (degrees)" (list 45 0 360 1 10 1 SF-SLIDER)
SF-TOGGLE "Shadow" TRUE
SF-TOGGLE "Shadow" #t ; v3 >>>
SF-COLOR "Background color" "white"
SF-COLOR "Sphere color" "red"
; v3 >>> only declare name of default brush
@ -156,7 +164,7 @@
SF-TEXT "Multi-line text" "Hello,\nWorld!"
SF-PATTERN "Pattern" "Maple Leaves"
SF-GRADIENT "Gradient" "Deep Sea"
SF-TOGGLE "Gradient reverse" FALSE
SF-TOGGLE "Gradient reverse" #f ; v3 >>>
SF-FONT "Font" "Agate"
SF-ADJUSTMENT "Font size (pixels)" '(50 1 1000 1 10 0 1)
SF-PALETTE "Palette" "Default"

View File

@ -0,0 +1,78 @@
; Old style script testing v3 binding of return values from PDB
; Modified from clothify.scm
; 1. Calls (script-fu-use-v3) very early
; 2. Uses v3 binding for PDB returns, i.e. elides many (car ()) from original
; Expect:
; this works
; after this plugin is invoked, all other /scripts still work, when invoked by menu item
; !!! You can't do this at top level, it affects all scripts in extension-script-fu
; (script-fu-use-v3)
(define (script-fu-testv3 timg tdrawable bx by azimuth elevation depth)
(script-fu-use-v3)
(let* (
(width (gimp-drawable-get-width tdrawable))
(height (gimp-drawable-get-height tdrawable))
(img (gimp-image-new width height RGB))
; (layer-two (gimp-layer-new img width height RGB-IMAGE "Y Dots" 100 LAYER-MODE-MULTIPLY))
(layer-one (gimp-layer-new img width height RGB-IMAGE "X Dots" 100 LAYER-MODE-NORMAL))
(layer-two 0)
(bump-layer 0)
)
(gimp-context-push)
(gimp-context-set-defaults)
(gimp-image-undo-disable img)
(gimp-image-insert-layer img layer-one 0 0)
(gimp-context-set-background '(255 255 255))
(gimp-drawable-edit-fill layer-one FILL-BACKGROUND)
(plug-in-noisify RUN-NONINTERACTIVE img layer-one FALSE 0.7 0.7 0.7 0.7)
(set! layer-two (gimp-layer-copy layer-one 0))
(gimp-layer-set-mode layer-two LAYER-MODE-MULTIPLY)
(gimp-image-insert-layer img layer-two 0 0)
(plug-in-gauss-rle RUN-NONINTERACTIVE img layer-one bx TRUE FALSE)
(plug-in-gauss-rle RUN-NONINTERACTIVE img layer-two by FALSE TRUE)
(gimp-image-flatten img)
; Note the inner call returns (<numeric length> <vector>)
; We still need cadr even in v3 language
(set! bump-layer (aref (cadr (gimp-image-get-selected-layers img)) 0))
(plug-in-c-astretch RUN-NONINTERACTIVE img bump-layer)
(plug-in-noisify RUN-NONINTERACTIVE img bump-layer FALSE 0.2 0.2 0.2 0.2)
(plug-in-bump-map RUN-NONINTERACTIVE img tdrawable bump-layer azimuth elevation depth 0 0 0 0 FALSE FALSE 0)
(gimp-image-delete img)
(gimp-displays-flush)
(gimp-context-pop)
)
)
(script-fu-register "script-fu-testv3"
_"Test script-fu-use-v3..."
_"Test use of script-fu-use-v3 in old scripts"
"lloyd konneker"
""
"2023"
"RGB* GRAY*"
SF-IMAGE "Input image" 0
SF-DRAWABLE "Input drawable" 0
SF-ADJUSTMENT _"Blur X" '(9 3 100 1 10 0 1)
SF-ADJUSTMENT _"Blur Y" '(9 3 100 1 10 0 1)
SF-ADJUSTMENT _"Azimuth" '(135 0 360 1 10 1 0)
SF-ADJUSTMENT _"Elevation" '(45 0 90 1 10 1 0)
SF-ADJUSTMENT _"Depth" '(3 1 50 1 10 0 1)
)
(script-fu-menu-register "script-fu-testv3"
"<Image>/Filters/Development/Script-Fu/Test")