Fixed utf8 issue, added reconnect, added properties, added notify_message signal

This commit is contained in:
Jesse van den Kieboom 2006-01-05 23:26:10 +00:00
parent c06362a750
commit 52f0c5a54e
2 changed files with 197 additions and 103 deletions

View File

@ -40,6 +40,10 @@ struct _GmWorldPrivate {
gboolean active;
guint activity;
gchar *buffer;
gboolean manual_disconnect;
time_t manual_disconnect_timeout;
guint reconnect_id;
time_t last_command;
GmOptions *options;
GmTriggers *triggers;
@ -53,6 +57,15 @@ struct _GmWorldPrivate {
gint fd_log;
};
/* Properties */
enum {
PROP_0,
PROP_NAME,
PROP_ACTIVE,
PROP_ACTIVITY
};
/* Signals */
enum {
@ -64,10 +77,8 @@ enum {
TEXT_RECEIVED,
EDITOR_ADDED,
EDITOR_REMOVED,
NAME_CHANGED,
ACTIVE_CHANGED,
ACTIVITY_CHANGED,
HIGHLIGHT,
NOTIFY_MESSAGE,
NUM_SIGNALS
};
@ -94,6 +105,10 @@ gm_world_finalize(GObject *object) {
if (world->priv->fd_log > 0) {
close(world->priv->fd_log);
}
if (world->priv->reconnect_id) {
g_source_remove(world->priv->reconnect_id);
}
gm_g_list_free_simple(world->priv->history);
@ -111,12 +126,41 @@ gm_world_finalize(GObject *object) {
G_OBJECT_CLASS(gm_world_parent_class)->finalize(object);
}
static void
gm_world_get_property(GObject *object, guint prop_id, GValue *value,
GParamSpec *pspec) {
GmWorld *world = GM_WORLD(object);
switch (prop_id) {
case PROP_NAME:
g_value_set_string(value, gm_world_name(world));
break;
case PROP_ACTIVE:
g_value_set_boolean(value, world->priv->active);
break;
case PROP_ACTIVITY:
g_value_set_int(value, world->priv->activity);
break;
}
}
static void
gm_world_class_init(GmWorldClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = gm_world_finalize;
object_class->get_property = gm_world_get_property;
g_object_class_install_property(object_class, PROP_NAME,
g_param_spec_string("name", "NAME", "The worlds name", NULL,
G_PARAM_READABLE));
g_object_class_install_property(object_class, PROP_ACTIVE,
g_param_spec_boolean("active", "ACTIVE", "If world is active",
FALSE, G_PARAM_READABLE));
g_object_class_install_property(object_class, PROP_ACTIVITY,
g_param_spec_boolean("activity", "ACTIVITY", "Lines of activity",
0, G_PARAM_READABLE));
world_signals[ACTIVATE_REQUEST] =
g_signal_new("activate_request",
G_OBJECT_CLASS_TYPE(object_class),
@ -203,39 +247,6 @@ gm_world_class_init(GmWorldClass *klass) {
1,
G_TYPE_OBJECT);
world_signals[NAME_CHANGED] =
g_signal_new("name_changed",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmWorldClass, name_changed),
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE,
1,
G_TYPE_STRING);
world_signals[ACTIVE_CHANGED] =
g_signal_new("active_changed",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmWorldClass, active_changed),
NULL, NULL,
g_cclosure_marshal_VOID__BOOLEAN,
G_TYPE_NONE,
1,
G_TYPE_BOOLEAN);
world_signals[ACTIVITY_CHANGED] =
g_signal_new("activity_changed",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmWorldClass, activity_changed),
NULL, NULL,
g_cclosure_marshal_VOID__INT,
G_TYPE_NONE,
1,
G_TYPE_INT);
world_signals[HIGHLIGHT] =
g_signal_new("highlight",
G_OBJECT_CLASS_TYPE(object_class),
@ -248,6 +259,17 @@ gm_world_class_init(GmWorldClass *klass) {
G_TYPE_INT,
G_TYPE_INT,
G_TYPE_STRING);
world_signals[NOTIFY_MESSAGE] =
g_signal_new("notify_message",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmWorldClass, notify_message),
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE,
1,
G_TYPE_STRING);
g_type_class_add_private(object_class, sizeof(GmWorldPrivate));
}
@ -308,7 +330,8 @@ gm_world_load_input_history(GmWorld *world) {
filename = g_strconcat(world->priv->path, G_DIR_SEPARATOR_S, "history",
NULL);
gm_debug_msg(DEBUG_DEFAULT, "GmWorld.LoadInputHistory: loading history (%s)!", filename);
gm_debug_msg(DEBUG_DEFAULT, "GmWorld.LoadInputHistory: loading history "
"(%s)!", filename);
if ((f = fopen(filename, "r")) != NULL) {
str = g_string_new("");
@ -316,7 +339,8 @@ gm_world_load_input_history(GmWorld *world) {
while (fgets(line, 1024 - 1, f) != NULL) {
g_string_append(str, line);
if (line[strlen(line) - 1] == '\n') {
if (line[1] != '\0') { // Empty lines, we don't need to process those
if (line[1] != '\0') {
// Empty lines, we don't need to process those
world->priv->history = g_list_append(world->priv->history,
g_strndup(str->str, strlen(str->str) - 1));
}
@ -380,6 +404,25 @@ gm_world_load_triggers(GmWorld *world) {
g_free(path);
}
static gboolean
gm_world_reconnect(GmWorld *world) {
world->priv->reconnect_id = 0;
const gchar *host, *port;
host = gm_net_current_host(world->priv->net);
port = gm_net_current_port(world->priv->net);
if (!host) {
host = gm_options_get(world->priv->options, "host");
}
if (!port) {
port = gm_options_get(world->priv->options, "port");
}
gm_world_connect_to(world, host, port);
return FALSE;
}
/* Public */
GmWorld *
gm_world_new(gchar *path) {
@ -389,10 +432,12 @@ gm_world_new(gchar *path) {
if (path != NULL) {
options_path = g_strconcat(path, "/settings", NULL);
gm_debug_msg(DEBUG_DEFAULT, "GmWorld.new: creating new world for %s", path);
gm_debug_msg(DEBUG_DEFAULT, "GmWorld.new: creating new world for %s",
path);
world->priv->path = g_strdup(path);
gm_debug_msg(DEBUG_DEFAULT, "GmWorld.new: creating default world settings for %s", path);
gm_debug_msg(DEBUG_DEFAULT, "GmWorld.new: creating default world "
"settings for %s", path);
gm_options_load(world->priv->options, options_path);
if (strlen(gm_options_get(world->priv->options, "charset")) == 0) {
@ -446,6 +491,11 @@ gm_world_unload(GmWorld *world) {
gm_world_remove_editor(world,
GM_EDITOR(world->priv->editors->data));
}
if (world->priv->reconnect_id) {
g_source_remove(world->priv->reconnect_id);
world->priv->reconnect_id = 0;
}
g_signal_emit(world, world_signals[UNLOAD], 0);
}
@ -507,34 +557,37 @@ gm_world_current_port(GmWorld *world) {
}
void
gm_world_connect(GmWorld *world) {
/*if (strlen(gm_options_get(world->priv->options, "host")) == 0) {
g_signal_emit(world, world_signals[WORLD_ERROR], 0,
_("World has no host, please fill in a host first"));
} else if (strlen(gm_options_get(world->priv->options, "port")) == 0) {
g_signal_emit(world, world_signals[WORLD_ERROR], 0,
_("World has no port, please fill in a port first"));
} else {*/
gm_net_connect(world->priv->net,
gm_options_get(world->priv->options, "host"),
gm_options_get(world->priv->options, "port"));
//}
}
gm_world_connect_to(GmWorld *world, gchar const *host, gchar const *port) {
if (world->priv->reconnect_id) {
g_source_remove(world->priv->reconnect_id);
}
void
gm_world_connect_to(GmWorld *world, gchar *host, gchar *port) {
gm_net_connect(world->priv->net, host, port);
}
void
gm_world_connect(GmWorld *world) {
gm_world_connect_to(world,
gm_options_get(world->priv->options, "host"),
gm_options_get(world->priv->options, "port"));
}
void
gm_world_disconnect(GmWorld *world) {
world->priv->manual_disconnect = TRUE;
world->priv->manual_disconnect_timeout = time(0) + 5;
gm_net_disconnect(world->priv->net);
}
void
gm_world_prepare_disconnect(GmWorld *world) {
world->priv->manual_disconnect = TRUE;
world->priv->manual_disconnect_timeout = time(0) + 5;
}
gboolean
gm_world_log_allowed(GmLogType type) {
GmOptions *options = gm_app_options(gm_app_instance());
// TODO: this can be optimized
switch (type) {
case LOG_IN:
@ -755,7 +808,7 @@ gm_world_apply_trigger(GmWorld *world, GmTrigger *trigger, gchar const *text,
break;
case TAT_NOTIFY:
tmp = gm_world_triggers_subst(data->data, text, matches);
// TODO
g_signal_emit(world, world_signals[NOTIFY_MESSAGE], 0, tmp);
g_free(tmp);
break;
case TAT_RUN_SCRIPT:
@ -810,16 +863,29 @@ gm_world_process_triggers(GmWorld *world, gchar *text) {
}
void
gm_world_process_line(GmWorld *world, gchar *line) {
gchar *non_text_start = NULL;
gm_world_process_line(GmWorld *world, gchar *line, gint len) {
gchar *non_text_start = NULL, *from;
GmEditingInfo *einfo = &(world->priv->editing_info);
GmEditor *editor;
gchar *no_ansi;
gboolean has_ansi_start;
if (strncmp(line, "\x1B[0m", 4) == 0) {
non_text_start = g_strdup(line + 4);
has_ansi_start = TRUE;
from = line + 4;
if (len != -1) {
len = len - 4;
}
} else {
non_text_start = g_strdup(line);
has_ansi_start = FALSE;
from = line;
}
if (len == -1) {
non_text_start = g_strdup(from);
} else {
non_text_start = g_strndup(from, len);
}
if (einfo->is_editing) {
@ -858,7 +924,7 @@ gm_world_process_line(GmWorld *world, gchar *line) {
}
if (strncmp(non_text_start, "#$\"", 3) == 0) {
if (strlen(non_text_start) != strlen(line)) {
if (has_ansi_start) {
g_signal_emit(world, world_signals[TEXT_RECEIVED], 0,
"\x1B[0m");
}
@ -870,11 +936,17 @@ gm_world_process_line(GmWorld *world, gchar *line) {
gm_world_log(world, LOG_IN, non_text_start + 3);
no_ansi = gm_ansi_strip(g_strdup(non_text_start + 3));
} else {
g_signal_emit(world, world_signals[TEXT_RECEIVED], 0, line);
if (has_ansi_start) {
g_signal_emit(world, world_signals[TEXT_RECEIVED], 0,
"\x1B[0m");
}
g_signal_emit(world, world_signals[TEXT_RECEIVED], 0,
non_text_start);
g_signal_emit(world, world_signals[TEXT_RECEIVED], 0, "\n");
gm_world_log(world, LOG_IN, line);
no_ansi = gm_ansi_strip(g_strdup(line));
gm_world_log(world, LOG_IN, non_text_start);
no_ansi = gm_ansi_strip(g_strdup(non_text_start));
}
/* Process triggers */
@ -903,8 +975,8 @@ gm_world_process_input(GmWorld *world, gchar *text) {
argstr = g_strdup(space + 1);
}
gm_debug_msg(DEBUG_DEFAULT, "GmWorld.ProcessInput: Trying script %s (%s)", script,
argstr);
gm_debug_msg(DEBUG_DEFAULT, "GmWorld.ProcessInput: Trying script "
"%s (%s)", script, argstr);
script_ran = gm_scripts_run(gm_app_scripts(gm_app_instance()),
world, script, argstr);
@ -973,6 +1045,7 @@ gm_world_sendln_log(GmWorld *world, gchar *text, GmLogType logtype) {
void
gm_world_sendln(GmWorld *world, gchar *text) {
world->priv->last_command = time(0);
gm_world_sendln_log(world, text, LOG_OUT);
}
@ -980,7 +1053,7 @@ void
gm_world_set_active(GmWorld *world, gboolean active) {
world->priv->active = active;
g_signal_emit(world, world_signals[ACTIVE_CHANGED], 0, active);
g_object_notify(G_OBJECT(world), "active");
if (active) {
gm_world_set_activity(world, 0);
@ -996,7 +1069,7 @@ void
gm_world_set_activity(GmWorld *world, gint activity) {
world->priv->activity = activity;
g_signal_emit(world, world_signals[ACTIVITY_CHANGED], 0, activity);
g_object_notify(G_OBJECT(world), "activity");
}
gint
@ -1039,8 +1112,7 @@ gm_world_name_changed(GmWorld *world) {
gm_triggers_save_as(world->priv->triggers, tmp_path);
g_free(tmp_path);
g_signal_emit(world, world_signals[NAME_CHANGED], 0,
gm_options_get(world->priv->options, "name"));
g_object_notify(G_OBJECT(world), "name");
}
GmMcpSession *
@ -1085,16 +1157,45 @@ on_gm_world_editor_save(GmEditor *editor, GmWorld *world) {
void
on_gm_world_net_state_changing(GmNet *net, GmNetState state, GmWorld *world) {
GmNetState prev_state = gm_net_state(net);
g_signal_emit(world, world_signals[STATE_CHANGING], 0, state);
if (state == GM_NET_STATE_DISCONNECTED) {
if ((prev_state == GM_NET_STATE_CONNECTED ||
prev_state == GM_NET_STATE_DISCONNECTING) &&
state == GM_NET_STATE_DISCONNECTED) {
gm_mcp_session_reset(world->priv->mcp);
#ifdef HAVE_RUBY
gm_scripts_run(gm_app_scripts(gm_app_instance()), world,
"on_disconnect", NULL);
#endif
if (!world->priv->reconnect_id &&
gm_options_get_int(world->priv->options, "reconnect")) {
// Manual disconnect timeout check
if (world->priv->manual_disconnect ||
time(0) < world->priv->manual_disconnect_timeout) {
return;
}
// Last command send check
if (difftime(time(0), world->priv->last_command) <= 3) {
return;
}
world->priv->reconnect_id = g_timeout_add(3000,
(GSourceFunc)gm_world_reconnect, world);
gm_world_writeln(world, _("# Reconnecting in 3 seconds..."));
}
}
}
void
on_gm_world_net_state_changed(GmNet *net, GmNetState state, GmWorld *world) {
if (state == GM_NET_STATE_CONNECTED) {
world->priv->manual_disconnect = FALSE;
gm_world_auto_login(world);
#ifdef HAVE_RUBY
@ -1102,13 +1203,6 @@ on_gm_world_net_state_changed(GmNet *net, GmNetState state, GmWorld *world) {
NULL);
#endif
}
if (state == GM_NET_STATE_DISCONNECTED) {
#ifdef HAVE_RUBY
gm_scripts_run(gm_app_scripts(gm_app_instance()), world,
"on_disconnect", NULL);
#endif
}
}
void
@ -1120,8 +1214,8 @@ on_gm_world_net_net_error(GmNet *net, gchar *error, gint code,
void
on_gm_world_net_bytes_recv(GmNet *net, gchar *text, gint len,
GmWorld *world) {
gchar *all, *utext, *ptr, *line, *p;
gint i;
gchar *all, *utext, *ptr, *start;
gunichar ch, prev;
utext = gm_to_utf8_with_fallback(text, len,
gm_options_get(world->priv->options, "charset"), "?");
@ -1140,28 +1234,29 @@ on_gm_world_net_bytes_recv(GmNet *net, gchar *text, gint len,
all = utext;
}
// TODO: UTF-8 compliant
line = (gchar *)(malloc((strlen(all) * sizeof(gchar)) + 1));
i = 0;
p = all;
ptr = start = all;
prev = 0;
/* Find lines in `all' and process them */
for (ptr = all; *ptr != '\0'; ptr++) {
if (*ptr == '\n') {
line[i] = '\0';
line[i + 1] = '\0';
while ((ch = g_utf8_get_char(ptr)) != '\0') {
if (ch == '\n') {
gm_world_process_line(world, start, ptr - start -
(prev == '\r' ? 1 : 0));
ptr = start = g_utf8_next_char(ptr);
gm_world_process_line(world, line);
p = ptr + 1;
i = 0;
} else if (*ptr != '\r') {
line[i] = *ptr;
i++;
// Skip \r
if (g_utf8_get_char(ptr) == '\r') {
ptr = start = g_utf8_next_char(ptr);
}
} else {
ptr = g_utf8_next_char(ptr);
}
prev = ch;
}
if (i > 0) {
world->priv->buffer = g_strdup(p);
if (*start != '\0') {
world->priv->buffer = g_strdup(start);
}
g_free(all);

View File

@ -90,11 +90,9 @@ struct _GmWorldClass {
void (* text_received) (GmWorld *world, gchar const *text);
void (* editor_added) (GmWorld *world, GObject *editor);
void (* editor_removed) (GmWorld *world, GObject *editor);
void (* name_changed) (GmWorld *world, gchar const *name);
void (* active_changed) (GmWorld *world, gboolean active);
void (* activity_changed) (GmWorld *world, gint activity);
void (* highlight) (GmWorld *world, gint start, gint end,
gchar const *color);
void (* notify_message) (GmWorld *world, gchar const *message);
};
GType gm_world_get_type(void) G_GNUC_CONST;
@ -127,8 +125,9 @@ gboolean gm_world_connected(GmWorld *world);
gboolean gm_world_disconnected(GmWorld *world);
void gm_world_connect(GmWorld *world);
void gm_world_connect_to(GmWorld *world, gchar *host, gchar *port);
void gm_world_connect_to(GmWorld *world, gchar const *host, gchar const *port);
void gm_world_disconnect(GmWorld *world);
void gm_world_prepare_disconnect(GmWorld *world);
void gm_world_add_editor(GmWorld *world, GmEditor *editor);
void gm_world_remove_editor(GmWorld *world, GmEditor *editor);
void gm_world_sendln_log(GmWorld *world, gchar *text, GmLogType logtype);