1 /*
2  * peas-extension.c
3  * This file is part of libpeas
4  *
5  * Copyright (C) 2010 Steve Frécinaux
6  *
7  * libpeas is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * libpeas is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
20  */
21 
22 #include "config.h"
23 
24 #include "peas-extension.h"
25 #include "peas-introspection.h"
26 
27 /**
28  * SECTION:peas-extension
29  * @short_description: Proxy for extensions.
30  * @see_also: #PeasExtensionSet
31  *
32  * #PeasExtension is a proxy class used to access actual extensions
33  * implemented using various languages.  As such, the application writer will
34  * use #PeasExtension instances to call methods on extension provided by
35  * loaded plugins.
36  *
37  * To properly use the proxy instances, you will need GObject-introspection
38  * data for the #GType you want to use as an extension point.
39  * For instance, if you wish to use #PeasActivatable, you will need to
40  * put the following code excerpt in the engine initialization code, in order
41  * to load the required "Peas" typelib:
42  *
43  * |[
44  * g_irepository_require (g_irepository_get_default (),
45  *                        "Peas", "1.0", 0, NULL);
46  * ]|
47  *
48  * You should proceed the same way for any namespace which provides types
49  * you want to use as extension points. GObject-introspection data is required
50  * for all the supported languages, even for C.
51  *
52  * #PeasExtension does not provide any way to access the underlying object.
53  * The main reason is that some loaders may not rely on proper GObject
54  * inheritance for the definition of extensions, and hence it would not be
55  * possible for libpeas to provide a functional GObject instance at all.
56  * Another reason is that it makes reference counting issues easier to deal
57  * with.
58  *
59  * See peas_extension_call() for more information.
60  **/
61 GType
peas_extension_get_type(void)62 peas_extension_get_type (void)
63 {
64   return G_TYPE_OBJECT;
65 }
66 
67 static
68 G_DEFINE_QUARK (peas-extension-type, extension_type)
69 
70 static GICallableInfo *
get_method_info(PeasExtension * exten,const gchar * method_name,GType * gtype)71 get_method_info (PeasExtension *exten,
72                  const gchar   *method_name,
73                  GType         *gtype)
74 {
75   guint i;
76   GType exten_type;
77   GType *interfaces;
78   GICallableInfo *method_info;
79 
80   /* Must prioritize the initial GType */
81   exten_type = peas_extension_get_extension_type (exten);
82   method_info = peas_gi_get_method_info (exten_type, method_name);
83 
84   if (method_info != NULL)
85     {
86       if (gtype != NULL)
87         *gtype = exten_type;
88 
89       return method_info;
90     }
91 
92   interfaces = g_type_interfaces (G_TYPE_FROM_INSTANCE (exten), NULL);
93 
94   for (i = 0; interfaces[i] != G_TYPE_INVALID; ++i)
95     {
96       method_info = peas_gi_get_method_info (interfaces[i], method_name);
97 
98       if (method_info != NULL)
99         {
100           if (gtype != NULL)
101             *gtype = interfaces[i];
102 
103           break;
104         }
105     }
106 
107   if (method_info == NULL)
108     g_warning ("Could not find the GType for method '%s'", method_name);
109 
110   g_free (interfaces);
111   return method_info;
112 }
113 
114 /**
115  * peas_extension_get_extension_type:
116  * @exten: A #PeasExtension.
117  *
118  * Get the #GType of the extension proxied by @exten.
119  *
120  * Return value: The #GType proxied by @exten.
121  *
122  * Deprecated: 1.2.
123  */
124 GType
peas_extension_get_extension_type(PeasExtension * exten)125 peas_extension_get_extension_type (PeasExtension *exten)
126 {
127   return GPOINTER_TO_SIZE (g_object_get_qdata (G_OBJECT (exten),
128                                                extension_type_quark ()));
129 }
130 
131 /**
132  * peas_extension_call:
133  * @exten: A #PeasExtension.
134  * @method_name: the name of the method that should be called.
135  * @...: arguments for the method.
136  *
137  * Call a method of the object behind @extension.
138  *
139  * The arguments provided to this functions should be of the same type as
140  * those defined in the #GInterface or #GObjectClass used as a base for the
141  * proxied extension. They should be provided in the same order, and if its
142  * return type is not void, then a pointer to a variable of that type should
143  * be passed as the last argument.
144  *
145  * For instance, if the method prototype is:
146  * |[ gint (*my_method) (MyClass *instance, const gchar *str, SomeObject *obj); ]|
147  * you should call peas_extension_call() this way:
148  * |[ peas_extension_call (extension, "my_method", "some_str", obj, &gint_var); ]|
149  *
150  * This function will not do anything if the introspection data for the proxied
151  * object's class has not been loaded previously through g_irepository_require().
152  *
153  * Return value: %TRUE on successful call.
154  *
155  * Deprecated: 1.2: Use the object directly instead.
156  */
157 gboolean
peas_extension_call(PeasExtension * exten,const gchar * method_name,...)158 peas_extension_call (PeasExtension *exten,
159                      const gchar   *method_name,
160                      ...)
161 {
162   va_list args;
163   gboolean result;
164 
165   g_return_val_if_fail (PEAS_IS_EXTENSION (exten), FALSE);
166   g_return_val_if_fail (method_name != NULL, FALSE);
167 
168   va_start (args, method_name);
169   result = peas_extension_call_valist (exten, method_name, args);
170   va_end (args);
171 
172   return result;
173 }
174 
175 /**
176  * peas_extension_call_valist:
177  * @exten: A #PeasExtension.
178  * @method_name: the name of the method that should be called.
179  * @args: the arguments for the method.
180  *
181  * Call a method of the object behind @extension, using @args as arguments.
182  *
183  * See peas_extension_call() for more information.
184  *
185  * Return value: %TRUE on successful call.
186  *
187  * Deprecated: 1.2: Use the object directly instead.
188  */
189 gboolean
peas_extension_call_valist(PeasExtension * exten,const gchar * method_name,va_list args)190 peas_extension_call_valist (PeasExtension *exten,
191                             const gchar   *method_name,
192                             va_list        args)
193 {
194   GICallableInfo *callable_info;
195   GITypeInfo retval_info;
196   GIArgument *gargs;
197   GIArgument retval;
198   gpointer retval_ptr;
199   gboolean ret;
200   gint n_args;
201 
202   g_return_val_if_fail (PEAS_IS_EXTENSION (exten), FALSE);
203   g_return_val_if_fail (method_name != NULL, FALSE);
204 
205   callable_info = get_method_info (exten, method_name, NULL);
206 
207   /* Already warned */
208   if (callable_info == NULL)
209     return FALSE;
210 
211   n_args = g_callable_info_get_n_args (callable_info);
212   g_return_val_if_fail (n_args >= 0, FALSE);
213   gargs = g_newa (GIArgument, n_args);
214   peas_gi_valist_to_arguments (callable_info, args, gargs, &retval_ptr);
215 
216   ret = peas_extension_callv (exten, method_name, gargs, &retval);
217 
218   if (retval_ptr != NULL)
219     {
220       g_callable_info_load_return_type (callable_info, &retval_info);
221       peas_gi_argument_to_pointer (&retval_info, &retval, retval_ptr);
222     }
223 
224   g_base_info_unref ((GIBaseInfo *) callable_info);
225 
226   return ret;
227 }
228 
229 /**
230  * peas_extension_callv:
231  * @exten: A #PeasExtension.
232  * @method_name: the name of the method that should be called.
233  * @args: the arguments for the method.
234  * @return_value: the return falue for the method.
235  *
236  * Call a method of the object behind @extension, using @args as arguments.
237  *
238  * See peas_extension_call() for more information.
239  *
240  * Return value: %TRUE on successful call.
241  *
242  * Deprecated: 1.2: Use the object directly instead.
243  */
244 gboolean
peas_extension_callv(PeasExtension * exten,const gchar * method_name,GIArgument * args,GIArgument * return_value)245 peas_extension_callv (PeasExtension *exten,
246                       const gchar   *method_name,
247                       GIArgument    *args,
248                       GIArgument    *return_value)
249 {
250   GICallableInfo *method_info;
251   GType gtype;
252   gboolean success;
253 
254   g_return_val_if_fail (PEAS_IS_EXTENSION (exten), FALSE);
255   g_return_val_if_fail (method_name != NULL, FALSE);
256 
257   method_info = get_method_info (exten, method_name, &gtype);
258 
259   /* Already warned */
260   if (method_info == NULL)
261     return FALSE;
262 
263   success = peas_gi_method_call (G_OBJECT (exten), method_info, gtype,
264                                  method_name, args, return_value);
265 
266   g_base_info_unref (method_info);
267   return success;
268 }
269