PIKApp/plug-ins/script-fu/libscriptfu/scheme-marshal.c

244 lines
8.0 KiB
C

/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* 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 "libpika/pika.h"
#include "tinyscheme/scheme-private.h"
#include "scheme-marshal.h"
#include "script-fu-errors.h"
/*
* Marshal arguments to, and return values from, calls to PDB.
* Convert Scheme constructs to/from a GValue.
*
* For each marshalling function:
* - a returned "pointer" is a scheme pointer to a foreign error or NULL.
* - marshal into a GValue holding a designated type,
* usually a PIKA type but also GLib types, e.g. GFile.
* The GValue's held type is already set, but value is uninitialized.
*
* When marshalling into a PikaObjectArray, arbitrarily say the contained type is PIKA_TYPE_DRAWABLE.
* The actual contained type is opaque to the PDB calling mechanism.
* Setting the GValue's value does not check the contained type.
* But we do call pika_drawable_get_by_id.
* PIKA_TYPE_DRAWABLE is a superclass of most common uses.
* But perhaps we should call pika_item_get_by_id
* and arbitrarily say PIKA_TYPE_ITEM, a superclass of drawable.
*/
/* Marshal single drawable ID from script into a single GObject. */
pointer
marshal_ID_to_drawable (scheme *sc,
pointer a,
gint id,
GValue *value)
{
PikaDrawable *drawable;
pointer error = get_drawable_from_script (sc, a, id, &drawable);
if (error)
return error;
/* drawable is NULL or valid */
/* Shallow copy, adding a reference while the GValue exists. */
g_value_set_object (value, drawable);
return NULL; /* no error */
}
/* Marshal a vector of ID into PikaObjectArray of same length. */
pointer
marshal_vector_to_drawable_array (scheme *sc,
pointer vector,
GValue *value)
{
PikaDrawable **drawable_array;
gint id;
pointer error;
GType actual_type = PIKA_TYPE_DRAWABLE;
guint num_elements = sc->vptr->vector_length (vector);
g_debug ("vector has %d elements", num_elements);
/* empty vector will produce empty PikaObjectArray */
drawable_array = g_new0 (PikaDrawable*, num_elements);
for (int j = 0; j < num_elements; ++j)
{
pointer element = sc->vptr->vector_elem (vector, j);
if (!sc->vptr->is_number (element))
{
g_free (drawable_array);
return script_error (sc, "Expected numeric in drawable vector", vector);
/* FUTURE more detailed error msg:
* return script_type_error_in_container (sc, "numeric", i, j, proc_name, vector);
*/
}
id = sc->vptr->ivalue (element);
error = get_drawable_from_script (sc, element, id, &drawable_array[j]);
if (error)
{
g_free (drawable_array);
return error;
}
/* Parameters are validated based on the actual type inside the object
array. So we set that type here instead of a generic drawable. */
if (j == 0)
actual_type = G_OBJECT_TYPE (drawable_array[j]);
}
/* Shallow copy. */
pika_value_set_object_array (value, actual_type, (GObject**)drawable_array, num_elements);
g_free (drawable_array);
return NULL; /* no error */
}
/* Marshal path string from script into a GValue holding type GFile */
void
marshal_path_string_to_gfile (scheme *sc,
pointer a,
GValue *value)
{
/* require sc->vptr->is_string (sc->vptr->pair_car (a)) */
GFile *gfile = g_file_new_for_path (sc->vptr->string_value (sc->vptr->pair_car (a)));
/* GLib docs say that g_file_new_for_path():
* "never fails, but the returned object might not support any I/O operation if path is malformed."
*/
g_value_set_object (value, gfile);
g_debug ("gfile arg is '%s'\n", g_file_get_parse_name (gfile));
}
/* Marshal values returned from PDB call in a GValue, into a Scheme construct to a script. */
/* Marshal a GValue holding a GFile into a string.
*
* Returns NULL or a string that must be freed.
*/
gchar *
marshal_returned_gfile_to_string (GValue *value)
{
gchar * filepath = NULL;
GObject *object = g_value_get_object (value);
/* object can be NULL, the GValue's type only indicates what should have been returned. */
if (object)
{
filepath = g_file_get_parse_name ((GFile *) object);
/* GLib docs:
* For local files with names that can safely be converted to UTF-8 the pathname is used,
* otherwise the IRI is used (a form of URI that allows UTF-8 characters unescaped).
*/
}
return filepath;
}
/* Marshal a PikaObjectArray into a Scheme list of ID's.
*
* Before v3.0, PDB procedure's return type was say INT32ARRAY,
* preceded by a type INT32 designating array length.
* Now return type is PikaObjectArray preceded by length.
*
* Returns a vector, since most arrays in Scriptfu are returned as vectors.
* An alternate implementation would be return list.
*
* Existing scheme plugins usually expect PDB to return values: len, vector.
* If ever the PDB is changed to be more object-oriented,
* scripts could use a scheme call: (vector-length vector)
* to get the length of the vector.
*/
pointer
marshal_returned_object_array_to_vector (scheme *sc,
GValue *value)
{
GObject **object_array;
gint32 n;
pointer vector;
object_array = pika_value_get_object_array (value);
/* array knows own length, ignore length in preceding return value */
n = ((PikaObjectArray*)g_value_get_boxed (value))->length;
vector = sc->vptr->mk_vector (sc, n);
/* Iterate starting at the back of the array, and prefix to container
* so the order of objects is not changed.
*/
for (int j = n - 1; j >= 0; j--)
{
GObject *object = object_array[j];
gint id;
if (object)
g_object_get (object, "id", &id, NULL); /* get property "id" */
else
/* Scriptfu language represents NULL object by ID of -1*/
id = -1;
sc->vptr->set_vector_elem (vector, j, sc->vptr->mk_integer (sc, id));
/* Alt: list = sc->vptr->cons (sc, sc->vptr->mk_integer (sc, id), list); */
}
/* ensure container's len equals object array's len and all elements are ID's or -1 */
return vector;
}
/* From a script numeric (a drawable ID) set a handle to a drawable.
* When ID is -1, sets drawable to NULL and returns no error.
* When ID is valid, sets drawable and returns no error.
* Otherwise (ID is not -1 and not valid ID of a drawable) returns error.
*/
pointer
get_drawable_from_script (scheme *sc,
pointer a,
gint id,
PikaDrawable **drawable_handle)
{
if (id == -1)
{
/* -1 is scriptfu language for NULL i.e. none for an optional */
*drawable_handle = NULL;
}
else
{
*drawable_handle = pika_drawable_get_by_id (id);
if (! *drawable_handle)
return script_error (sc, "Invalid drawable ID", a);
}
/* ensure *drawable_handle is NULL or a valid reference to a drawable */
return NULL; /* no error */
}