1 | #include <gio/gio.h> |
2 | #include <stdlib.h> |
3 | |
4 | /* ---------------------------------------------------------------------------------------------------- */ |
5 | |
6 | /* The object we want to export */ |
7 | typedef struct _MyObjectClass MyObjectClass; |
8 | typedef struct _MyObject MyObject; |
9 | |
10 | struct _MyObjectClass |
11 | { |
12 | GObjectClass parent_class; |
13 | }; |
14 | |
15 | struct _MyObject |
16 | { |
17 | GObject parent_instance; |
18 | |
19 | gint count; |
20 | gchar *name; |
21 | }; |
22 | |
23 | enum |
24 | { |
25 | PROP_0, |
26 | PROP_COUNT, |
27 | PROP_NAME |
28 | }; |
29 | |
30 | static GType my_object_get_type (void); |
31 | G_DEFINE_TYPE (MyObject, my_object, G_TYPE_OBJECT) |
32 | |
33 | static void |
34 | my_object_finalize (GObject *object) |
35 | { |
36 | MyObject *myobj = (MyObject*)object; |
37 | |
38 | g_free (mem: myobj->name); |
39 | |
40 | G_OBJECT_CLASS (my_object_parent_class)->finalize (object); |
41 | } |
42 | |
43 | static void |
44 | my_object_init (MyObject *object) |
45 | { |
46 | object->count = 0; |
47 | object->name = NULL; |
48 | } |
49 | |
50 | static void |
51 | my_object_get_property (GObject *object, |
52 | guint prop_id, |
53 | GValue *value, |
54 | GParamSpec *pspec) |
55 | { |
56 | MyObject *myobj = (MyObject*)object; |
57 | |
58 | switch (prop_id) |
59 | { |
60 | case PROP_COUNT: |
61 | g_value_set_int (value, v_int: myobj->count); |
62 | break; |
63 | |
64 | case PROP_NAME: |
65 | g_value_set_string (value, v_string: myobj->name); |
66 | break; |
67 | |
68 | default: |
69 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
70 | } |
71 | } |
72 | |
73 | static void |
74 | my_object_set_property (GObject *object, |
75 | guint prop_id, |
76 | const GValue *value, |
77 | GParamSpec *pspec) |
78 | { |
79 | MyObject *myobj = (MyObject*)object; |
80 | |
81 | switch (prop_id) |
82 | { |
83 | case PROP_COUNT: |
84 | myobj->count = g_value_get_int (value); |
85 | break; |
86 | |
87 | case PROP_NAME: |
88 | g_free (mem: myobj->name); |
89 | myobj->name = g_value_dup_string (value); |
90 | break; |
91 | |
92 | default: |
93 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
94 | } |
95 | } |
96 | |
97 | static void |
98 | my_object_class_init (MyObjectClass *class) |
99 | { |
100 | GObjectClass *gobject_class = G_OBJECT_CLASS (class); |
101 | |
102 | gobject_class->finalize = my_object_finalize; |
103 | gobject_class->set_property = my_object_set_property; |
104 | gobject_class->get_property = my_object_get_property; |
105 | |
106 | g_object_class_install_property (oclass: gobject_class, |
107 | property_id: PROP_COUNT, |
108 | pspec: g_param_spec_int (name: "count" , |
109 | nick: "Count" , |
110 | blurb: "Count" , |
111 | minimum: 0, maximum: 99999, default_value: 0, |
112 | flags: G_PARAM_READWRITE)); |
113 | |
114 | g_object_class_install_property (oclass: gobject_class, |
115 | property_id: PROP_NAME, |
116 | pspec: g_param_spec_string (name: "name" , |
117 | nick: "Name" , |
118 | blurb: "Name" , |
119 | NULL, |
120 | flags: G_PARAM_READWRITE)); |
121 | } |
122 | |
123 | /* A method that we want to export */ |
124 | static void |
125 | my_object_change_count (MyObject *myobj, |
126 | gint change) |
127 | { |
128 | myobj->count = 2 * myobj->count + change; |
129 | |
130 | g_object_notify (G_OBJECT (myobj), property_name: "count" ); |
131 | } |
132 | |
133 | /* ---------------------------------------------------------------------------------------------------- */ |
134 | |
135 | static GDBusNodeInfo *introspection_data = NULL; |
136 | |
137 | /* Introspection data for the service we are exporting */ |
138 | static const gchar introspection_xml[] = |
139 | "<node>" |
140 | " <interface name='org.myorg.MyObject'>" |
141 | " <method name='ChangeCount'>" |
142 | " <arg type='i' name='change' direction='in'/>" |
143 | " </method>" |
144 | " <property type='i' name='Count' access='read'/>" |
145 | " <property type='s' name='Name' access='readwrite'/>" |
146 | " </interface>" |
147 | "</node>" ; |
148 | |
149 | |
150 | static void |
151 | handle_method_call (GDBusConnection *connection, |
152 | const gchar *sender, |
153 | const gchar *object_path, |
154 | const gchar *interface_name, |
155 | const gchar *method_name, |
156 | GVariant *parameters, |
157 | GDBusMethodInvocation *invocation, |
158 | gpointer user_data) |
159 | { |
160 | MyObject *myobj = user_data; |
161 | |
162 | if (g_strcmp0 (str1: method_name, str2: "ChangeCount" ) == 0) |
163 | { |
164 | gint change; |
165 | g_variant_get (value: parameters, format_string: "(i)" , &change); |
166 | |
167 | my_object_change_count (myobj, change); |
168 | |
169 | g_dbus_method_invocation_return_value (invocation, NULL); |
170 | } |
171 | } |
172 | |
173 | static GVariant * |
174 | handle_get_property (GDBusConnection *connection, |
175 | const gchar *sender, |
176 | const gchar *object_path, |
177 | const gchar *interface_name, |
178 | const gchar *property_name, |
179 | GError **error, |
180 | gpointer user_data) |
181 | { |
182 | GVariant *ret; |
183 | MyObject *myobj = user_data; |
184 | |
185 | ret = NULL; |
186 | if (g_strcmp0 (str1: property_name, str2: "Count" ) == 0) |
187 | { |
188 | ret = g_variant_new_int32 (value: myobj->count); |
189 | } |
190 | else if (g_strcmp0 (str1: property_name, str2: "Name" ) == 0) |
191 | { |
192 | ret = g_variant_new_string (string: myobj->name ? myobj->name : "" ); |
193 | } |
194 | |
195 | return ret; |
196 | } |
197 | |
198 | static gboolean |
199 | handle_set_property (GDBusConnection *connection, |
200 | const gchar *sender, |
201 | const gchar *object_path, |
202 | const gchar *interface_name, |
203 | const gchar *property_name, |
204 | GVariant *value, |
205 | GError **error, |
206 | gpointer user_data) |
207 | { |
208 | MyObject *myobj = user_data; |
209 | |
210 | if (g_strcmp0 (str1: property_name, str2: "Count" ) == 0) |
211 | { |
212 | g_object_set (object: myobj, first_property_name: "count" , g_variant_get_int32 (value), NULL); |
213 | } |
214 | else if (g_strcmp0 (str1: property_name, str2: "Name" ) == 0) |
215 | { |
216 | g_object_set (object: myobj, first_property_name: "name" , g_variant_get_string (value, NULL), NULL); |
217 | } |
218 | |
219 | return TRUE; |
220 | } |
221 | |
222 | |
223 | /* for now */ |
224 | static const GDBusInterfaceVTable interface_vtable = |
225 | { |
226 | handle_method_call, |
227 | handle_get_property, |
228 | handle_set_property |
229 | }; |
230 | |
231 | static void |
232 | send_property_change (GObject *obj, |
233 | GParamSpec *pspec, |
234 | GDBusConnection *connection) |
235 | { |
236 | GVariantBuilder *builder; |
237 | GVariantBuilder *invalidated_builder; |
238 | MyObject *myobj = (MyObject *)obj; |
239 | |
240 | builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); |
241 | invalidated_builder = g_variant_builder_new (G_VARIANT_TYPE ("as" )); |
242 | |
243 | if (g_strcmp0 (str1: pspec->name, str2: "count" ) == 0) |
244 | g_variant_builder_add (builder, |
245 | format_string: "{sv}" , |
246 | "Count" , g_variant_new_int32 (value: myobj->count)); |
247 | else if (g_strcmp0 (str1: pspec->name, str2: "name" ) == 0) |
248 | g_variant_builder_add (builder, |
249 | format_string: "{sv}" , |
250 | "Name" , g_variant_new_string (string: myobj->name ? myobj->name : "" )); |
251 | |
252 | g_dbus_connection_emit_signal (connection, |
253 | NULL, |
254 | object_path: "/org/myorg/MyObject" , |
255 | interface_name: "org.freedesktop.DBus.Properties" , |
256 | signal_name: "PropertiesChanged" , |
257 | parameters: g_variant_new (format_string: "(sa{sv}as)" , |
258 | "org.myorg.MyObject" , |
259 | builder, |
260 | invalidated_builder), |
261 | NULL); |
262 | } |
263 | |
264 | static void |
265 | on_bus_acquired (GDBusConnection *connection, |
266 | const gchar *name, |
267 | gpointer user_data) |
268 | { |
269 | MyObject *myobj = user_data; |
270 | guint registration_id; |
271 | |
272 | g_signal_connect (myobj, "notify" , |
273 | G_CALLBACK (send_property_change), connection); |
274 | registration_id = g_dbus_connection_register_object (connection, |
275 | object_path: "/org/myorg/MyObject" , |
276 | interface_info: introspection_data->interfaces[0], |
277 | vtable: &interface_vtable, |
278 | user_data: myobj, |
279 | NULL, /* user_data_free_func */ |
280 | NULL); /* GError** */ |
281 | g_assert (registration_id > 0); |
282 | } |
283 | |
284 | static void |
285 | on_name_acquired (GDBusConnection *connection, |
286 | const gchar *name, |
287 | gpointer user_data) |
288 | { |
289 | } |
290 | |
291 | static void |
292 | on_name_lost (GDBusConnection *connection, |
293 | const gchar *name, |
294 | gpointer user_data) |
295 | { |
296 | exit (status: 1); |
297 | } |
298 | |
299 | int |
300 | main (int argc, char *argv[]) |
301 | { |
302 | guint owner_id; |
303 | GMainLoop *loop; |
304 | MyObject *myobj; |
305 | |
306 | /* We are lazy here - we don't want to manually provide |
307 | * the introspection data structures - so we just build |
308 | * them from XML. |
309 | */ |
310 | introspection_data = g_dbus_node_info_new_for_xml (xml_data: introspection_xml, NULL); |
311 | g_assert (introspection_data != NULL); |
312 | |
313 | myobj = g_object_new (object_type: my_object_get_type (), NULL); |
314 | |
315 | owner_id = g_bus_own_name (bus_type: G_BUS_TYPE_SESSION, |
316 | name: "org.myorg.MyObject" , |
317 | flags: G_BUS_NAME_OWNER_FLAGS_NONE, |
318 | bus_acquired_handler: on_bus_acquired, |
319 | name_acquired_handler: on_name_acquired, |
320 | name_lost_handler: on_name_lost, |
321 | user_data: myobj, |
322 | NULL); |
323 | |
324 | loop = g_main_loop_new (NULL, FALSE); |
325 | g_main_loop_run (loop); |
326 | |
327 | g_bus_unown_name (owner_id); |
328 | |
329 | g_dbus_node_info_unref (info: introspection_data); |
330 | |
331 | g_object_unref (object: myobj); |
332 | |
333 | return 0; |
334 | } |
335 | |