From 52f0c5a54e1ca23db5a3ff14895860ea2e955817 Mon Sep 17 00:00:00 2001 From: Jesse van den Kieboom Date: Thu, 5 Jan 2006 23:26:10 +0000 Subject: [PATCH] Fixed utf8 issue, added reconnect, added properties, added notify_message signal --- gnoemoe/gm-world.c | 293 ++++++++++++++++++++++++++++++--------------- gnoemoe/gm-world.h | 7 +- 2 files changed, 197 insertions(+), 103 deletions(-) diff --git a/gnoemoe/gm-world.c b/gnoemoe/gm-world.c index b0026e4..5567d08 100644 --- a/gnoemoe/gm-world.c +++ b/gnoemoe/gm-world.c @@ -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); diff --git a/gnoemoe/gm-world.h b/gnoemoe/gm-world.h index 3848068..cfbd9f9 100644 --- a/gnoemoe/gm-world.h +++ b/gnoemoe/gm-world.h @@ -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);