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, >ype);
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