/*
roxterm - GTK+ 2.0 terminal emulator with tabs
Copyright (C) 2004 Tony Houghton <h@realh.co.uk>
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 2 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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "display.h"
#include "dlg.h"
#include "globalopts.h"
#include "optsdbus.h"
#ifdef ROXTERM_CAPPLET
#include "colourgui.h"
#include "configlet.h"
#include "profilegui.h"
#endif
#include <string.h>
#define DBUS_API_SUBJECT_TO_CHANGE
#include <dbus/dbus-glib-lowlevel.h>
#define OPTSDBUS_NAME RTDBUS_NAME ".Options"
#define OPTSDBUS_OBJECT_PATH RTDBUS_OBJECT_PATH "/Options"
#define OPTSDBUS_INTERFACE RTDBUS_INTERFACE ".Options"
#define OPTSDBUS_ERROR RTDBUS_ERROR ".OptionsError"
#define OPTSDBUS_STRING_SIGNAL "StringOption"
#define OPTSDBUS_INT_SIGNAL "IntOption"
#define OPTSDBUS_FLOAT_SIGNAL "FloatOption"
#define OPTSDBUS_SET_PROFILE "SetProfile"
#define OPTSDBUS_SET_COLOUR_SCHEME "SetColourScheme"
#define OPTSDBUS_SET_SHORTCUT_SCHEME "SetShortcutScheme"
#ifdef ROXTERM_CAPPLET
static gboolean optsdbus_read_args(DBusMessage *message,
char RTDBUS_ARG_CONST **arg, char RTDBUS_ARG_CONST **display,
DBusError *perror, DBusMessage **preply_error)
{
gboolean result = arg ? dbus_message_get_args(message, perror,
DBUS_TYPE_STRING, arg,
DBUS_TYPE_STRING, display,
DBUS_TYPE_INVALID) :
dbus_message_get_args(message, perror,
DBUS_TYPE_STRING, display,
DBUS_TYPE_INVALID);
if (!result)
{
rtdbus_whinge(perror, _("Unable to get argument from D-BUS message"));
if (!dbus_message_get_no_reply(message))
{
*preply_error = dbus_message_new_error(message, OPTSDBUS_ERROR,
_("Unable to get argument"));
}
return FALSE;
}
return TRUE;
}
static DBusHandlerResult optsdbus_method_handler(DBusConnection *connection,
DBusMessage *message, void *user_data)
{
DBusMessage *reply = NULL;
DBusError derror;
enum {
Unknown,
EditProfile,
EditColourScheme,
OpenConfiglet
} action = Unknown;
RTDBUS_ARG_CONST char *profile_name = NULL;
RTDBUS_ARG_CONST char *display_name = NULL;
char RTDBUS_ARG_CONST **parg = NULL;
GdkScreen *scrn = NULL;
dbus_error_init(&derror);
if (dbus_message_is_method_call(message, OPTSDBUS_INTERFACE, "EditProfile"))
{
action = EditProfile;
}
else if (dbus_message_is_method_call(message, OPTSDBUS_INTERFACE,
"EditColourScheme"))
{
action = EditColourScheme;
}
else if (dbus_message_is_method_call(message, OPTSDBUS_INTERFACE,
"Configlet"))
{
action = OpenConfiglet;
}
if (action == EditProfile || action == EditColourScheme)
parg = &profile_name;
switch (action)
{
case EditProfile:
case EditColourScheme:
case OpenConfiglet:
if (!optsdbus_read_args(message, parg, &display_name,
&derror, &reply))
{
break;
}
if (display_name && display_name[0])
scrn = display_get_screen_for_name(display_name);
/* Fall-through */
switch (action)
{
case EditProfile:
profilegui_open(profile_name, scrn);
break;
case EditColourScheme:
colourgui_open(profile_name, scrn);
break;
case OpenConfiglet:
configlet_open(scrn);
break;
default:
break;
}
if (!dbus_message_get_no_reply(message))
reply = dbus_message_new_method_return(message);
#if ROXTERM_DBUS_OLD_ARGS_SEMANTICS
if (profile_name)
dbus_free(profile_name);
if (display_name)
dbus_free(display_name);
#endif
break;
default:
g_warning(_("Don't know how to handle method %s.%s"),
dbus_message_get_interface(message),
dbus_message_get_member(message));
break;
}
if (reply)
rtdbus_send_message(reply);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static DBusMessage *optsdbus_signal_new(const char *signal_name,
const char *profile_name, const char *key)
{
return rtdbus_signal_new(OPTSDBUS_OBJECT_PATH, OPTSDBUS_INTERFACE,
signal_name,
DBUS_TYPE_STRING, RTDBUS_ARG(profile_name),
DBUS_TYPE_STRING, RTDBUS_ARG(key),
DBUS_TYPE_INVALID);
}
gboolean optsdbus_send_string_opt_signal(const char *profile_name,
const char *key, const char *value)
{
DBusMessage *message = optsdbus_signal_new(OPTSDBUS_STRING_SIGNAL,
profile_name, key);
if (message)
{
DBusError derror;
dbus_error_init(&derror);
if (!value)
value = "";
if (!rtdbus_append_args(message,
DBUS_TYPE_STRING, RTDBUS_ARG(value), DBUS_TYPE_INVALID))
{
return FALSE;
}
else
{
return rtdbus_send_message(message);
}
}
return FALSE;
}
gboolean optsdbus_send_int_opt_signal(const char *profile_name,
const char *key, int value)
{
DBusMessage *message;
message = optsdbus_signal_new(OPTSDBUS_INT_SIGNAL,
profile_name, key);
if (message)
{
DBusError derror;
dbus_error_init(&derror);
if (!dbus_message_append_args(message,
DBUS_TYPE_INT32, RTDBUS_ARG(value), DBUS_TYPE_INVALID))
{
return FALSE;
}
else
{
return rtdbus_send_message(message);
}
}
return FALSE;
}
gboolean optsdbus_send_float_opt_signal(const char *profile_name,
const char *key, double value)
{
DBusMessage *message;
message = optsdbus_signal_new(OPTSDBUS_FLOAT_SIGNAL,
profile_name, key);
if (message)
{
DBusError derror;
dbus_error_init(&derror);
if (!dbus_message_append_args(message,
DBUS_TYPE_DOUBLE, RTDBUS_ARG(value), DBUS_TYPE_INVALID))
{
return FALSE;
}
else
{
return rtdbus_send_message(message);
}
}
return FALSE;
}
gboolean optsdbus_send_stuff_changed_signal(const char *what_happened,
const char *family_name, const char *old_name, const char *new_name)
{
/* We can cheat and use optsdbus_signal_new because it just happens
* to build a message with 2 string args; just what we want */
DBusMessage *message = optsdbus_signal_new(what_happened,
family_name, old_name);
if (message)
{
DBusError derror;
dbus_error_init(&derror);
if (new_name)
{
if (!dbus_message_append_args(message,
DBUS_TYPE_STRING, RTDBUS_ARG(new_name), DBUS_TYPE_INVALID))
{
return FALSE;
}
}
return rtdbus_send_message(message);
}
return FALSE;
}
gboolean optsdbus_send_edit_opts_message(const char *method, const char *arg,
const char *display_name)
{
DBusMessage * message;
if (!display_name)
display_name = "";
message = rtdbus_method_new(OPTSDBUS_NAME,
OPTSDBUS_OBJECT_PATH, OPTSDBUS_INTERFACE, method,
DBUS_TYPE_STRING, RTDBUS_ARG(arg),
DBUS_TYPE_STRING, RTDBUS_ARG(display_name),
DBUS_TYPE_INVALID);
if (!message)
return FALSE;
return rtdbus_send_message(message);
}
gboolean optsdbus_init(void)
{
return rtdbus_start_service(OPTSDBUS_NAME, OPTSDBUS_OBJECT_PATH,
optsdbus_method_handler, FALSE);
}
#else /* !ROXTERM_CAPPLET */
static OptsDBusOptionHandler optsdbus_option_handler = NULL;
static OptsDBusStuffChangedHandler optsdbus_stuff_changed_handler = NULL;
static OptsDBusSetProfileHandler optsdbus_set_profile_handler = NULL;
static OptsDBusSetProfileHandler optsdbus_set_colour_scheme_handler = NULL;
static OptsDBusSetProfileHandler optsdbus_set_shortcut_scheme_handler = NULL;
static gboolean optsdbus_set_profile_callback(OptsDBusSetProfileHandler handler,
DBusMessage *message, DBusError *pderror)
{
RTDBUS_ARG_CONST char *id_str = NULL;
RTDBUS_ARG_CONST char *profile_name = NULL;
gboolean result = dbus_message_get_args(message, pderror,
DBUS_TYPE_STRING, &id_str,
DBUS_TYPE_STRING, &profile_name,
DBUS_TYPE_INVALID);
if (result)
{
void *id = NULL;
if (sscanf(id_str, "%p", &id) == 1)
{
handler(id, profile_name);
}
else
{
dlg_warning(NULL,
_("Unrecognised ROXTERM_ID '%s' in D-Bus message"), id_str);
}
}
#if ROXTERM_DBUS_OLD_ARGS_SEMANTICS
if (profile_name)
dbus_free(profile_name);
if (id_str)
dbus_free(id_str);
#endif
return result;
}
static DBusHandlerResult
optsdbus_message_filter(DBusConnection * connection, DBusMessage * message,
void *user_data)
{
RTDBUS_ARG_CONST char *profile_name = NULL;
RTDBUS_ARG_CONST char *key = NULL;
OptsDBusValue val;
char *dbus_str = NULL;
const char *signal_name;
gboolean args_result = TRUE;
DBusError derror;
OptsDBusOptType opt_type = OptsDBus_InvalidOpt;
const char *what_happened = NULL;
RTDBUS_ARG_CONST char *family_name = NULL;
RTDBUS_ARG_CONST char *current_name = NULL;
RTDBUS_ARG_CONST char *new_name = NULL;
if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL ||
strcmp(dbus_message_get_path(message), OPTSDBUS_OBJECT_PATH))
{
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
signal_name = dbus_message_get_member(message);
if (strcmp(dbus_message_get_interface(message), OPTSDBUS_INTERFACE))
{
goto bad_signal;
}
dbus_error_init(&derror);
if (!strcmp(signal_name, OPTSDBUS_STRING_SIGNAL))
{
args_result = dbus_message_get_args(message, &derror,
DBUS_TYPE_STRING, &profile_name,
DBUS_TYPE_STRING, &key,
DBUS_TYPE_STRING, &dbus_str, DBUS_TYPE_INVALID);
if (args_result)
{
opt_type = OptsDBus_StringOpt;
val.s = dbus_str && dbus_str[0] ? dbus_str : NULL;
}
}
else if (!strcmp(signal_name, OPTSDBUS_INT_SIGNAL))
{
args_result = dbus_message_get_args(message, &derror,
DBUS_TYPE_STRING, &profile_name,
DBUS_TYPE_STRING, &key,
DBUS_TYPE_INT32, &val.i, DBUS_TYPE_INVALID);
if (args_result)
opt_type = OptsDBus_IntOpt;
}
else if (!strcmp(signal_name, OPTSDBUS_FLOAT_SIGNAL))
{
args_result = dbus_message_get_args(message, &derror,
DBUS_TYPE_STRING, &profile_name,
DBUS_TYPE_STRING, &key,
DBUS_TYPE_DOUBLE, &val.f, DBUS_TYPE_INVALID);
if (args_result)
opt_type = OptsDBus_FloatOpt;
}
else if (!strcmp(signal_name, OPTSDBUS_DELETED)
|| !strcmp(signal_name, OPTSDBUS_ADDED)
|| !strcmp(signal_name, OPTSDBUS_RENAMED))
{
what_happened = signal_name;
if (strcmp(signal_name, OPTSDBUS_RENAMED))
{
args_result = dbus_message_get_args(message, &derror,
DBUS_TYPE_STRING, &family_name,
DBUS_TYPE_STRING, ¤t_name,
DBUS_TYPE_INVALID);
}
else
{
args_result = dbus_message_get_args(message, &derror,
DBUS_TYPE_STRING, &family_name,
DBUS_TYPE_STRING, ¤t_name,
DBUS_TYPE_STRING, &new_name,
DBUS_TYPE_INVALID);
}
}
else if (!strcmp(signal_name, OPTSDBUS_SET_PROFILE))
{
if (optsdbus_set_profile_handler)
{
args_result = optsdbus_set_profile_callback(
optsdbus_set_profile_handler, message, &derror);
}
}
else if (!strcmp(signal_name, OPTSDBUS_SET_COLOUR_SCHEME))
{
if (optsdbus_set_colour_scheme_handler)
{
args_result = optsdbus_set_profile_callback(
optsdbus_set_colour_scheme_handler, message, &derror);
}
}
else if (!strcmp(signal_name, OPTSDBUS_SET_SHORTCUT_SCHEME))
{
if (optsdbus_set_shortcut_scheme_handler)
{
args_result = optsdbus_set_profile_callback(
optsdbus_set_shortcut_scheme_handler, message, &derror);
}
}
else
{
goto bad_signal;
}
if (!args_result)
{
rtdbus_whinge(&derror, _("Unable to read D-BUS signal arguments"));
}
else
{
if (profile_name && optsdbus_option_handler)
{
(*optsdbus_option_handler) (profile_name, key, opt_type, val);
}
if (family_name && optsdbus_stuff_changed_handler)
{
(*optsdbus_stuff_changed_handler) (what_happened,
family_name, current_name, new_name);
}
}
#if ROXTERM_DBUS_OLD_ARGS_SEMANTICS
if (profile_name)
dbus_free(profile_name);
if (key)
dbus_free(key);
if (dbus_str)
dbus_free(dbus_str);
if (family_name)
dbus_free(family_name);
if (current_name)
dbus_free(current_name);
if (new_name)
dbus_free(new_name);
#endif
return DBUS_HANDLER_RESULT_HANDLED;
bad_signal:
g_warning(_("Unrecognised D-BUS signal %s.%s"),
dbus_message_get_interface(message), signal_name);
/* Consider it handled, because nothing else should be handling
* this object */
return DBUS_HANDLER_RESULT_HANDLED;
}
static void optsdbus_enable_filter(void)
{
static gboolean enabled = FALSE;
if (enabled)
return;
enabled = TRUE;
rtdbus_add_signal_rule_and_filter(
OPTSDBUS_OBJECT_PATH, OPTSDBUS_INTERFACE,
optsdbus_message_filter);
}
void optsdbus_listen_for_opt_signals(OptsDBusOptionHandler handler)
{
g_return_if_fail(rtdbus_ok);
optsdbus_enable_filter();
optsdbus_option_handler = handler;
}
void optsdbus_listen_for_stuff_changed_signals(
OptsDBusStuffChangedHandler handler)
{
g_return_if_fail(rtdbus_ok);
optsdbus_enable_filter();
optsdbus_stuff_changed_handler = handler;
}
void optsdbus_listen_for_set_profile_signals(OptsDBusSetProfileHandler handler)
{
g_return_if_fail(rtdbus_ok);
optsdbus_enable_filter();
optsdbus_set_profile_handler = handler;
}
void optsdbus_listen_for_set_colour_scheme_signals(
OptsDBusSetProfileHandler handler)
{
g_return_if_fail(rtdbus_ok);
optsdbus_enable_filter();
optsdbus_set_colour_scheme_handler = handler;
}
void optsdbus_listen_for_set_shortcut_scheme_signals(
OptsDBusSetProfileHandler handler)
{
g_return_if_fail(rtdbus_ok);
optsdbus_enable_filter();
optsdbus_set_shortcut_scheme_handler = handler;
}
gboolean optsdbus_send_edit_opts_message(const char *method, const char *arg,
const char *display_name)
{
GError *error = NULL;
char *appdir = global_options_appdir && global_options_appdir[0] ?
g_strdup_printf("'--appdir=%s'", global_options_appdir) : NULL;
char *bindir = global_options_bindir ?
g_strdup_printf("%s%c", global_options_bindir, G_DIR_SEPARATOR) :
NULL;
char *disp = display_name ? g_strconcat("--display=", display_name, NULL) :
NULL;
char *param = arg ? g_strdup_printf("--%s=%s", method, arg) :
g_strdup(method);
char *command = g_strdup_printf("%sroxterm-config %s %s '%s'",
bindir ? bindir : "", appdir ? appdir : "",
disp ? disp : "", param);
g_free(bindir);
g_free(appdir);
g_free(disp);
g_free(param);
if (!g_spawn_command_line_async(command, &error))
{
dlg_critical(NULL, _("Error launching capplet: %s"), error->message);
g_error_free(error);
g_free(command);
return FALSE;
}
g_free(command);
return TRUE;
}
#endif /* !ROXTERM_CAPPLET */
/* vi:set sw=4 ts=4 noet cindent cino= */