1 /*
2 roxterm - VTE/GTK terminal emulator with tabs
3 Copyright (C) 2004-2015 Tony Houghton <h@realh.co.uk>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20
21 #include "dlg.h"
22 #include "globalopts.h"
23 #include "optsdbus.h"
24
25 #ifdef ROXTERM_CAPPLET
26 #include "colourgui.h"
27 #include "configlet.h"
28 #include "profilegui.h"
29 #endif
30
31 #include <string.h>
32
33 #define DBUS_API_SUBJECT_TO_CHANGE
34 #include <dbus/dbus-glib-lowlevel.h>
35
36 #define OPTSDBUS_NAME RTDBUS_NAME ".Options"
37 #define OPTSDBUS_OBJECT_PATH RTDBUS_OBJECT_PATH "/Options"
38 #define OPTSDBUS_INTERFACE RTDBUS_INTERFACE ".Options"
39 #define OPTSDBUS_ERROR RTDBUS_ERROR ".OptionsError"
40
41 #define OPTSDBUS_STRING_SIGNAL "StringOption"
42 #define OPTSDBUS_INT_SIGNAL "IntOption"
43 #define OPTSDBUS_FLOAT_SIGNAL "FloatOption"
44
45 #define OPTSDBUS_SET_PROFILE "SetProfile"
46 #define OPTSDBUS_SET_COLOUR_SCHEME "SetColourScheme"
47 #define OPTSDBUS_SET_SHORTCUT_SCHEME "SetShortcutScheme"
48
49 #ifdef ROXTERM_CAPPLET
50
optsdbus_read_args(DBusMessage * message,char const ** arg,DBusError * perror,DBusMessage ** preply_error)51 static gboolean optsdbus_read_args(DBusMessage *message, char const **arg,
52 DBusError *perror, DBusMessage **preply_error)
53 {
54 gboolean result = arg ? dbus_message_get_args(message, perror,
55 DBUS_TYPE_STRING, arg,
56 DBUS_TYPE_INVALID) :
57 dbus_message_get_args(message, perror,
58 DBUS_TYPE_INVALID);
59 if (!result)
60 {
61 rtdbus_whinge(perror, _("Unable to get argument from D-BUS message"));
62 if (!dbus_message_get_no_reply(message))
63 {
64 *preply_error = dbus_message_new_error(message, OPTSDBUS_ERROR,
65 _("Unable to get argument"));
66 }
67 return FALSE;
68 }
69 return TRUE;
70 }
71
optsdbus_method_handler(DBusConnection * connection,DBusMessage * message,void * user_data)72 static DBusHandlerResult optsdbus_method_handler(DBusConnection *connection,
73 DBusMessage *message, void *user_data)
74 {
75 DBusMessage *reply = NULL;
76 DBusError derror;
77 enum {
78 Unknown,
79 EditProfile,
80 EditColourScheme,
81 OpenConfiglet
82 } action = Unknown;
83 const char *profile_name = NULL;
84 char const **parg = NULL;
85 (void) connection;
86 (void) user_data;
87
88 dbus_error_init(&derror);
89
90 if (dbus_message_is_method_call(message, OPTSDBUS_INTERFACE, "EditProfile"))
91 {
92 action = EditProfile;
93 }
94 else if (dbus_message_is_method_call(message, OPTSDBUS_INTERFACE,
95 "EditColourScheme"))
96 {
97 action = EditColourScheme;
98 }
99 else if (dbus_message_is_method_call(message, OPTSDBUS_INTERFACE,
100 "Configlet"))
101 {
102 action = OpenConfiglet;
103 }
104 if (action == EditProfile || action == EditColourScheme)
105 parg = &profile_name;
106 switch (action)
107 {
108 case EditProfile:
109 case EditColourScheme:
110 case OpenConfiglet:
111 if (!optsdbus_read_args(message, parg, &derror, &reply))
112 {
113 break;
114 }
115 /* Fall-through */
116 switch (action)
117 {
118 case EditProfile:
119 profilegui_open(profile_name);
120 break;
121 case EditColourScheme:
122 colourgui_open(profile_name);
123 break;
124 case OpenConfiglet:
125 configlet_open();
126 break;
127 default:
128 break;
129 }
130 if (!dbus_message_get_no_reply(message))
131 reply = dbus_message_new_method_return(message);
132 #if ROXTERM_DBUS_OLD_ARGS_SEMANTICS
133 if (profile_name)
134 dbus_free(profile_name);
135 #endif
136 break;
137 default:
138 g_warning(_("Don't know how to handle method %s.%s"),
139 dbus_message_get_interface(message),
140 dbus_message_get_member(message));
141 break;
142 }
143
144 if (reply)
145 rtdbus_send_message(reply);
146
147 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
148 }
149
optsdbus_signal_new(const char * signal_name,const char * profile_name,const char * key)150 static DBusMessage *optsdbus_signal_new(const char *signal_name,
151 const char *profile_name, const char *key)
152 {
153 return rtdbus_signal_new(OPTSDBUS_OBJECT_PATH, OPTSDBUS_INTERFACE,
154 signal_name,
155 DBUS_TYPE_STRING, &profile_name,
156 DBUS_TYPE_STRING, &key,
157 DBUS_TYPE_INVALID);
158 }
159
optsdbus_send_string_opt_signal(const char * profile_name,const char * key,const char * value)160 gboolean optsdbus_send_string_opt_signal(const char *profile_name,
161 const char *key, const char *value)
162 {
163 DBusMessage *message = optsdbus_signal_new(OPTSDBUS_STRING_SIGNAL,
164 profile_name, key);
165
166 if (message)
167 {
168 DBusError derror;
169
170 dbus_error_init(&derror);
171 if (!value)
172 value = "";
173 if (!rtdbus_append_args(message,
174 DBUS_TYPE_STRING, &value, DBUS_TYPE_INVALID))
175 {
176 return FALSE;
177 }
178 else
179 {
180 return rtdbus_send_message(message);
181 }
182 }
183 return FALSE;
184 }
185
optsdbus_send_int_opt_signal(const char * profile_name,const char * key,int value)186 gboolean optsdbus_send_int_opt_signal(const char *profile_name,
187 const char *key, int value)
188 {
189 DBusMessage *message;
190
191 message = optsdbus_signal_new(OPTSDBUS_INT_SIGNAL,
192 profile_name, key);
193
194 if (message)
195 {
196 DBusError derror;
197
198 dbus_error_init(&derror);
199 if (!dbus_message_append_args(message,
200 DBUS_TYPE_INT32, &value, DBUS_TYPE_INVALID))
201 {
202 return FALSE;
203 }
204 else
205 {
206 return rtdbus_send_message(message);
207 }
208 }
209 return FALSE;
210 }
211
optsdbus_send_float_opt_signal(const char * profile_name,const char * key,double value)212 gboolean optsdbus_send_float_opt_signal(const char *profile_name,
213 const char *key, double value)
214 {
215 DBusMessage *message;
216
217 message = optsdbus_signal_new(OPTSDBUS_FLOAT_SIGNAL,
218 profile_name, key);
219
220 if (message)
221 {
222 DBusError derror;
223
224 dbus_error_init(&derror);
225 if (!dbus_message_append_args(message,
226 DBUS_TYPE_DOUBLE, &value, DBUS_TYPE_INVALID))
227 {
228 return FALSE;
229 }
230 else
231 {
232 return rtdbus_send_message(message);
233 }
234 }
235 return FALSE;
236 }
237
optsdbus_send_stuff_changed_signal(const char * what_happened,const char * family_name,const char * old_name,const char * new_name)238 gboolean optsdbus_send_stuff_changed_signal(const char *what_happened,
239 const char *family_name, const char *old_name, const char *new_name)
240 {
241 /* We can cheat and use optsdbus_signal_new because it just happens
242 * to build a message with 2 string args; just what we want */
243 DBusMessage *message = optsdbus_signal_new(what_happened,
244 family_name, old_name);
245
246 if (message)
247 {
248 DBusError derror;
249
250 dbus_error_init(&derror);
251 if (new_name)
252 {
253 if (!dbus_message_append_args(message,
254 DBUS_TYPE_STRING, &new_name, DBUS_TYPE_INVALID))
255 {
256 return FALSE;
257 }
258 }
259 return rtdbus_send_message(message);
260 }
261 return FALSE;
262 }
263
optsdbus_send_edit_opts_message(const char * method,const char * arg)264 gboolean optsdbus_send_edit_opts_message(const char *method, const char *arg)
265 {
266 DBusMessage * message;
267
268 message = rtdbus_method_new(OPTSDBUS_NAME,
269 OPTSDBUS_OBJECT_PATH, OPTSDBUS_INTERFACE, method,
270 DBUS_TYPE_STRING, &arg,
271 DBUS_TYPE_INVALID);
272
273 if (!message)
274 return FALSE;
275 return rtdbus_send_message(message);
276 }
277
optsdbus_init(void)278 gboolean optsdbus_init(void)
279 {
280 return rtdbus_start_service(OPTSDBUS_NAME, OPTSDBUS_OBJECT_PATH,
281 optsdbus_method_handler, FALSE);
282 }
283
284 #else /* !ROXTERM_CAPPLET */
285
286 static OptsDBusOptionHandler optsdbus_option_handler = NULL;
287 static OptsDBusStuffChangedHandler optsdbus_stuff_changed_handler = NULL;
288 static OptsDBusSetProfileHandler optsdbus_set_profile_handler = NULL;
289 static OptsDBusSetProfileHandler optsdbus_set_colour_scheme_handler = NULL;
290 static OptsDBusSetProfileHandler optsdbus_set_shortcut_scheme_handler = NULL;
291
optsdbus_set_profile_callback(OptsDBusSetProfileHandler handler,DBusMessage * message,DBusError * pderror)292 static gboolean optsdbus_set_profile_callback(OptsDBusSetProfileHandler handler,
293 DBusMessage *message, DBusError *pderror)
294 {
295 const char *id_str = NULL;
296 const char *profile_name = NULL;
297
298 gboolean result = dbus_message_get_args(message, pderror,
299 DBUS_TYPE_STRING, &id_str,
300 DBUS_TYPE_STRING, &profile_name,
301 DBUS_TYPE_INVALID);
302 if (result)
303 {
304 void *id = NULL;
305
306 if (sscanf(id_str, "%p", &id) == 1)
307 {
308 handler(id, profile_name);
309 }
310 else
311 {
312 dlg_warning(NULL,
313 _("Unrecognised ROXTERM_ID '%s' in D-Bus message"), id_str);
314 }
315 }
316 #if ROXTERM_DBUS_OLD_ARGS_SEMANTICS
317 if (profile_name)
318 dbus_free(profile_name);
319 if (id_str)
320 dbus_free(id_str);
321 #endif
322 return result;
323 }
324
325 static DBusHandlerResult
optsdbus_message_filter(DBusConnection * connection,DBusMessage * message,void * user_data)326 optsdbus_message_filter(DBusConnection * connection, DBusMessage * message,
327 void *user_data)
328 {
329 const char *profile_name = NULL;
330 const char *key = NULL;
331 OptsDBusValue val;
332 char *dbus_str = NULL;
333 const char *signal_name;
334 gboolean args_result = TRUE;
335 DBusError derror;
336 OptsDBusOptType opt_type = OptsDBus_InvalidOpt;
337 const char *what_happened = NULL;
338 const char *family_name = NULL;
339 const char *current_name = NULL;
340 const char *new_name = NULL;
341
342 (void) connection;
343 (void) user_data;
344
345 if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL ||
346 strcmp(dbus_message_get_path(message), OPTSDBUS_OBJECT_PATH))
347 {
348 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
349 }
350 signal_name = dbus_message_get_member(message);
351 if (strcmp(dbus_message_get_interface(message), OPTSDBUS_INTERFACE))
352 {
353 goto bad_signal;
354 }
355
356 dbus_error_init(&derror);
357 if (!strcmp(signal_name, OPTSDBUS_STRING_SIGNAL))
358 {
359 args_result = dbus_message_get_args(message, &derror,
360 DBUS_TYPE_STRING, &profile_name,
361 DBUS_TYPE_STRING, &key,
362 DBUS_TYPE_STRING, &dbus_str, DBUS_TYPE_INVALID);
363 if (args_result)
364 {
365 opt_type = OptsDBus_StringOpt;
366 val.s = dbus_str && dbus_str[0] ? dbus_str : NULL;
367 }
368 }
369 else if (!strcmp(signal_name, OPTSDBUS_INT_SIGNAL))
370 {
371 args_result = dbus_message_get_args(message, &derror,
372 DBUS_TYPE_STRING, &profile_name,
373 DBUS_TYPE_STRING, &key,
374 DBUS_TYPE_INT32, &val.i, DBUS_TYPE_INVALID);
375 if (args_result)
376 opt_type = OptsDBus_IntOpt;
377 }
378 else if (!strcmp(signal_name, OPTSDBUS_FLOAT_SIGNAL))
379 {
380 args_result = dbus_message_get_args(message, &derror,
381 DBUS_TYPE_STRING, &profile_name,
382 DBUS_TYPE_STRING, &key,
383 DBUS_TYPE_DOUBLE, &val.f, DBUS_TYPE_INVALID);
384 if (args_result)
385 opt_type = OptsDBus_FloatOpt;
386 }
387 else if (!strcmp(signal_name, OPTSDBUS_DELETED)
388 || !strcmp(signal_name, OPTSDBUS_ADDED)
389 || !strcmp(signal_name, OPTSDBUS_RENAMED)
390 || !strcmp(signal_name, OPTSDBUS_CHANGED))
391 {
392 what_happened = signal_name;
393 if (strcmp(signal_name, OPTSDBUS_RENAMED))
394 {
395 args_result = dbus_message_get_args(message, &derror,
396 DBUS_TYPE_STRING, &family_name,
397 DBUS_TYPE_STRING, ¤t_name,
398 DBUS_TYPE_INVALID);
399 }
400 else
401 {
402 args_result = dbus_message_get_args(message, &derror,
403 DBUS_TYPE_STRING, &family_name,
404 DBUS_TYPE_STRING, ¤t_name,
405 DBUS_TYPE_STRING, &new_name,
406 DBUS_TYPE_INVALID);
407 }
408 }
409 else if (!strcmp(signal_name, OPTSDBUS_SET_PROFILE))
410 {
411 if (optsdbus_set_profile_handler)
412 {
413 args_result = optsdbus_set_profile_callback(
414 optsdbus_set_profile_handler, message, &derror);
415 }
416 }
417 else if (!strcmp(signal_name, OPTSDBUS_SET_COLOUR_SCHEME))
418 {
419 if (optsdbus_set_colour_scheme_handler)
420 {
421 args_result = optsdbus_set_profile_callback(
422 optsdbus_set_colour_scheme_handler, message, &derror);
423 }
424 }
425 else if (!strcmp(signal_name, OPTSDBUS_SET_SHORTCUT_SCHEME))
426 {
427 if (optsdbus_set_shortcut_scheme_handler)
428 {
429 args_result = optsdbus_set_profile_callback(
430 optsdbus_set_shortcut_scheme_handler, message, &derror);
431 }
432 }
433 else
434 {
435 goto bad_signal;
436 }
437 if (!args_result)
438 {
439 rtdbus_whinge(&derror, _("Unable to read D-BUS signal arguments"));
440 }
441 else
442 {
443 if (profile_name && optsdbus_option_handler)
444 {
445 (*optsdbus_option_handler) (profile_name, key, opt_type, val);
446 }
447 if (family_name && optsdbus_stuff_changed_handler)
448 {
449 (*optsdbus_stuff_changed_handler) (what_happened,
450 family_name, current_name, new_name);
451 }
452 }
453
454 #if ROXTERM_DBUS_OLD_ARGS_SEMANTICS
455 if (profile_name)
456 dbus_free(profile_name);
457 if (key)
458 dbus_free(key);
459 if (dbus_str)
460 dbus_free(dbus_str);
461 if (family_name)
462 dbus_free(family_name);
463 if (current_name)
464 dbus_free(current_name);
465 if (new_name)
466 dbus_free(new_name);
467 #endif
468 return DBUS_HANDLER_RESULT_HANDLED;
469 bad_signal:
470 g_warning(_("Unrecognised D-BUS signal %s.%s"),
471 dbus_message_get_interface(message), signal_name);
472 /* Consider it handled, because nothing else should be handling
473 * this object */
474 return DBUS_HANDLER_RESULT_HANDLED;
475 }
476
optsdbus_enable_filter(void)477 static void optsdbus_enable_filter(void)
478 {
479 static gboolean enabled = FALSE;
480
481 if (enabled)
482 return;
483 enabled = TRUE;
484 rtdbus_add_signal_rule_and_filter(
485 OPTSDBUS_OBJECT_PATH, OPTSDBUS_INTERFACE,
486 optsdbus_message_filter);
487 }
488
optsdbus_listen_for_opt_signals(OptsDBusOptionHandler handler)489 void optsdbus_listen_for_opt_signals(OptsDBusOptionHandler handler)
490 {
491 g_return_if_fail(rtdbus_ok);
492 optsdbus_enable_filter();
493 optsdbus_option_handler = handler;
494 }
495
optsdbus_listen_for_stuff_changed_signals(OptsDBusStuffChangedHandler handler)496 void optsdbus_listen_for_stuff_changed_signals(
497 OptsDBusStuffChangedHandler handler)
498 {
499 g_return_if_fail(rtdbus_ok);
500 optsdbus_enable_filter();
501 optsdbus_stuff_changed_handler = handler;
502 }
503
optsdbus_listen_for_set_profile_signals(OptsDBusSetProfileHandler handler)504 void optsdbus_listen_for_set_profile_signals(OptsDBusSetProfileHandler handler)
505 {
506 g_return_if_fail(rtdbus_ok);
507 optsdbus_enable_filter();
508 optsdbus_set_profile_handler = handler;
509 }
510
optsdbus_listen_for_set_colour_scheme_signals(OptsDBusSetProfileHandler handler)511 void optsdbus_listen_for_set_colour_scheme_signals(
512 OptsDBusSetProfileHandler handler)
513 {
514 g_return_if_fail(rtdbus_ok);
515 optsdbus_enable_filter();
516 optsdbus_set_colour_scheme_handler = handler;
517 }
518
optsdbus_listen_for_set_shortcut_scheme_signals(OptsDBusSetProfileHandler handler)519 void optsdbus_listen_for_set_shortcut_scheme_signals(
520 OptsDBusSetProfileHandler handler)
521 {
522 g_return_if_fail(rtdbus_ok);
523 optsdbus_enable_filter();
524 optsdbus_set_shortcut_scheme_handler = handler;
525 }
526
optsdbus_send_edit_opts_message(const char * method,const char * arg)527 gboolean optsdbus_send_edit_opts_message(const char *method, const char *arg)
528 {
529 GError *error = NULL;
530 char *appdir = global_options_appdir && global_options_appdir[0] ?
531 g_strdup_printf("'--appdir=%s'", global_options_appdir) : NULL;
532 char *bindir = global_options_bindir ?
533 g_strdup_printf("%s%c", global_options_bindir, G_DIR_SEPARATOR) :
534 NULL;
535 char *param = arg ? g_strdup_printf("--%s=%s", method, arg) :
536 g_strdup(method);
537 char *command = g_strdup_printf("%sroxterm-config %s '%s'",
538 bindir ? bindir : "", appdir ? appdir : "", param);
539
540 g_free(bindir);
541 g_free(appdir);
542 g_free(param);
543 if (!g_spawn_command_line_async(command, &error))
544 {
545 dlg_critical(NULL, _("Error launching capplet: %s"), error->message);
546 g_error_free(error);
547 g_free(command);
548 return FALSE;
549 }
550 g_free(command);
551 return TRUE;
552 }
553
554 #endif /* !ROXTERM_CAPPLET */
555
556 /* vi:set sw=4 ts=4 noet cindent cino= */
557