changeset 13842:a9ff4499d9ce

[gaim-migrate @ 16295] Hopefully improve the typing notification code so it's a lot easier to understand. This also creates a distinction between the signals emitted when receiving GAIM_TYPED and GAIM_NOT_TYPING messages (by adding a gaim-typed signal). And the gaim-not-typing signal should work in all cases. Most of this is stuff I changed last week during work, thanks to Meebo committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Tue, 20 Jun 2006 08:17:49 +0000
parents f3d4a9902ead
children 2a7a13cac678
files plugins/perl/common/Conversation.xs plugins/perl/common/Server.xs src/conversation.c src/conversation.h src/gtkconv.c src/gtkpounce.c src/pounce.c src/pounce.h src/protocols/jabber/message.c src/protocols/jabber/message.h src/protocols/msn/msn.c src/protocols/novell/novell.c src/protocols/oscar/oscar.c src/protocols/sametime/sametime.c src/protocols/simple/simple.c src/protocols/yahoo/yahoo.c src/protocols/zephyr/zephyr.c src/prpl.h src/server.c src/server.h
diffstat 20 files changed, 225 insertions(+), 118 deletions(-) [+]
line wrap: on
line diff
--- a/plugins/perl/common/Conversation.xs	Tue Jun 20 04:05:56 2006 +0000
+++ b/plugins/perl/common/Conversation.xs	Tue Jun 20 08:17:49 2006 +0000
@@ -179,15 +179,15 @@
 	Gaim::Conversation::IM im
 
 void
-gaim_conv_im_start_type_again_timeout(im)
+gaim_conv_im_start_send_typed_timeout(im)
 	Gaim::Conversation::IM im
 
 void
-gaim_conv_im_stop_type_again_timeout(im)
+gaim_conv_im_stop_send_typed_timeout(im)
 	Gaim::Conversation::IM im
 
 guint
-gaim_conv_im_get_type_again_timeout(im)
+gaim_conv_im_get_send_typed_timeout(im)
 	Gaim::Conversation::IM im
 
 void
--- a/plugins/perl/common/Server.xs	Tue Jun 20 04:05:56 2006 +0000
+++ b/plugins/perl/common/Server.xs	Tue Jun 20 08:17:49 2006 +0000
@@ -200,10 +200,10 @@
 	Gaim::MessageFlags flags
 
 int  
-serv_send_typing(con, a, b)
+serv_send_typing(con, a, state)
 	Gaim::Connection con
 	const char * a
-	int b
+	Gaim::TypingState state
 
 void 
 serv_set_buddyicon(gc, filename)
--- a/src/conversation.c	Tue Jun 20 04:05:56 2006 +0000
+++ b/src/conversation.c	Tue Jun 20 08:17:49 2006 +0000
@@ -46,39 +46,44 @@
 }
 
 static gboolean
-reset_typing(gpointer data)
+reset_typing_cb(gpointer data)
 {
 	GaimConversation *c = (GaimConversation *)data;
 	GaimConvIm *im;
 
-	if (!g_list_find(conversations, c))
-		return FALSE;
-
 	im = GAIM_CONV_IM(c);
 
 	gaim_conv_im_set_typing_state(im, GAIM_NOT_TYPING);
 	gaim_conv_im_update_typing(im);
 	gaim_conv_im_stop_typing_timeout(im);
 
+	gaim_signal_emit(gaim_conversations_get_handle(),
+					 "buddy-typing-stopped", c);
+
 	return FALSE;
 }
 
 static gboolean
-send_typed(gpointer data)
+send_typed_cb(gpointer data)
 {
 	GaimConversation *conv = (GaimConversation *)data;
 	GaimConnection *gc;
 	const char *name;
 
 	g_return_val_if_fail(conv != NULL, FALSE);
-	
+
 	gc   = gaim_conversation_get_gc(conv);
 	name = gaim_conversation_get_name(conv);
 
 	if (gc != NULL && name != NULL) {
-		gaim_conv_im_set_type_again(GAIM_CONV_IM(conv), TRUE);
+		/* We set this to 1 so that GAIM_TYPING will be sent
+		 * if the Gaim user types anything else.
+		 */
+		gaim_conv_im_set_type_again(GAIM_CONV_IM(conv), 1);
 
 		serv_send_typing(gc, name, GAIM_TYPED);
+		gaim_signal_emit(gaim_conversations_get_handle(),
+						 "buddy-typed", conv);
 
 		gaim_debug(GAIM_DEBUG_MISC, "conversation", "typed...\n");
 	}
@@ -420,7 +425,7 @@
 
 	if (conv->type == GAIM_CONV_TYPE_IM) {
 		gaim_conv_im_stop_typing_timeout(conv->u.im);
-		gaim_conv_im_stop_type_again_timeout(conv->u.im);
+		gaim_conv_im_stop_send_typed_timeout(conv->u.im);
 
 		if (conv->u.im->icon != NULL)
 			gaim_buddy_icon_unref(conv->u.im->icon);
@@ -986,7 +991,12 @@
 			gaim_signal_emit(gaim_conversations_get_handle(),
 							 "buddy-typing", im->conv->account, im->conv->name);
 		}
-		else
+		else if (state == GAIM_TYPED)
+		{
+			gaim_signal_emit(gaim_conversations_get_handle(),
+							 "buddy-typed", im->conv->account, im->conv->name);
+		}
+		else if (state == GAIM_NOT_TYPING)
 		{
 			gaim_signal_emit(gaim_conversations_get_handle(),
 							 "buddy-typing-stopped", im->conv->account, im->conv->name);
@@ -1016,7 +1026,7 @@
 	conv = gaim_conv_im_get_conversation(im);
 	name = gaim_conversation_get_name(conv);
 
-	im->typing_timeout = gaim_timeout_add(timeout * 1000, reset_typing, conv);
+	im->typing_timeout = gaim_timeout_add(timeout * 1000, reset_typing_cb, conv);
 }
 
 void
@@ -1040,11 +1050,14 @@
 }
 
 void
-gaim_conv_im_set_type_again(GaimConvIm *im, time_t val)
+gaim_conv_im_set_type_again(GaimConvIm *im, unsigned int val)
 {
 	g_return_if_fail(im != NULL);
 
-	im->type_again = val;
+	if (val == 0)
+		im->type_again = 0;
+	else
+		im->type_again = time(NULL) + val;
 }
 
 time_t
@@ -1056,32 +1069,32 @@
 }
 
 void
-gaim_conv_im_start_type_again_timeout(GaimConvIm *im)
+gaim_conv_im_start_send_typed_timeout(GaimConvIm *im)
 {
 	g_return_if_fail(im != NULL);
 
-	im->type_again_timeout = gaim_timeout_add(SEND_TYPED_TIMEOUT, send_typed,
+	im->send_typed_timeout = gaim_timeout_add(SEND_TYPED_TIMEOUT, send_typed_cb,
 											  gaim_conv_im_get_conversation(im));
 }
 
 void
-gaim_conv_im_stop_type_again_timeout(GaimConvIm *im)
+gaim_conv_im_stop_send_typed_timeout(GaimConvIm *im)
 {
 	g_return_if_fail(im != NULL);
 
-	if (im->type_again_timeout == 0)
+	if (im->send_typed_timeout == 0)
 		return;
 
-	gaim_timeout_remove(im->type_again_timeout);
-	im->type_again_timeout = 0;
+	gaim_timeout_remove(im->send_typed_timeout);
+	im->send_typed_timeout = 0;
 }
 
 guint
-gaim_conv_im_get_type_again_timeout(const GaimConvIm *im)
+gaim_conv_im_get_send_typed_timeout(const GaimConvIm *im)
 {
 	g_return_val_if_fail(im != NULL, 0);
 
-	return im->type_again_timeout;
+	return im->send_typed_timeout;
 }
 
 void
@@ -2090,6 +2103,12 @@
 										GAIM_SUBTYPE_ACCOUNT),
 						 gaim_value_new(GAIM_TYPE_STRING));
 
+	gaim_signal_register(handle, "buddy-typed",
+						 gaim_marshal_VOID__POINTER_POINTER, NULL, 2,
+						 gaim_value_new(GAIM_TYPE_SUBTYPE,
+										GAIM_SUBTYPE_ACCOUNT),
+						 gaim_value_new(GAIM_TYPE_STRING));
+
 	gaim_signal_register(handle, "buddy-typing-stopped",
 						 gaim_marshal_VOID__POINTER_POINTER, NULL, 2,
 						 gaim_value_new(GAIM_TYPE_SUBTYPE,
--- a/src/conversation.h	Tue Jun 20 04:05:56 2006 +0000
+++ b/src/conversation.h	Tue Jun 20 08:17:49 2006 +0000
@@ -186,7 +186,7 @@
 	GaimTypingState typing_state;      /**< The current typing state.    */
 	guint  typing_timeout;             /**< The typing timer handle.     */
 	time_t type_again;                 /**< The type again time.         */
-	guint  type_again_timeout;         /**< The type again timer handle. */
+	guint  send_typed_timeout;         /**< The type again timer handle. */
 
 	GaimBuddyIcon *icon;               /**< The buddy icon.              */
 };
@@ -666,19 +666,25 @@
 guint gaim_conv_im_get_typing_timeout(const GaimConvIm *im);
 
 /**
- * Sets the IM's time until it should send another typing notification.
+ * Sets the quiet-time when no GAIM_TYPING messages will be sent.
+ * Few protocols need this (maybe only MSN).  If the user is still
+ * typing after this quiet-period, then another GAIM_TYPING message
+ * will be sent.
  *
  * @param im  The IM.
- * @param val The time.
+ * @param val The number of seconds to wait before allowing another
+ *            GAIM_TYPING message to be sent to the user.  Or 0 to
+ *            not send another GAIM_TYPING message.
  */
-void gaim_conv_im_set_type_again(GaimConvIm *im, time_t val);
+void gaim_conv_im_set_type_again(GaimConvIm *im, unsigned int val);
 
 /**
- * Returns the IM's time until it should send another typing notification.
+ * Returns the time after which another GAIM_TYPING message should be sent.
  *
  * @param im The IM.
  *
- * @return The time.
+ * @return The time in seconds since the epoch.  Or 0 if no additional
+ *         GAIM_TYPING message should be sent.
  */
 time_t gaim_conv_im_get_type_again(const GaimConvIm *im);
 
@@ -687,14 +693,14 @@
  *
  * @param im      The IM.
  */
-void gaim_conv_im_start_type_again_timeout(GaimConvIm *im);
+void gaim_conv_im_start_send_typed_timeout(GaimConvIm *im);
 
 /**
  * Stops the IM's type again timeout.
  *
  * @param im The IM.
  */
-void gaim_conv_im_stop_type_again_timeout(GaimConvIm *im);
+void gaim_conv_im_stop_send_typed_timeout(GaimConvIm *im);
 
 /**
  * Returns the IM's type again timeout interval.
@@ -703,7 +709,7 @@
  *
  * @return The type again timeout interval.
  */
-guint gaim_conv_im_get_type_again_timeout(const GaimConvIm *im);
+guint gaim_conv_im_get_send_typed_timeout(const GaimConvIm *im);
 
 /**
  * Updates the visual typing notification for an IM conversation.
--- a/src/gtkconv.c	Tue Jun 20 04:05:56 2006 +0000
+++ b/src/gtkconv.c	Tue Jun 20 08:17:49 2006 +0000
@@ -2168,8 +2168,7 @@
 	if (gtk_text_iter_is_start(start_pos) && gtk_text_iter_is_end(end_pos)) {
 
 		/* We deleted all the text, so turn off typing. */
-		if (gaim_conv_im_get_type_again_timeout(im))
-			gaim_conv_im_stop_type_again_timeout(im);
+		gaim_conv_im_stop_send_typed_timeout(im);
 
 		serv_send_typing(gaim_conversation_get_gc(conv),
 						 gaim_conversation_get_name(conv),
@@ -2913,22 +2912,18 @@
 
 	im = GAIM_CONV_IM(conv);
 
-	if (gaim_conv_im_get_type_again_timeout(im))
-		gaim_conv_im_stop_type_again_timeout(im);
-
-	gaim_conv_im_start_type_again_timeout(im);
-
+	gaim_conv_im_stop_send_typed_timeout(im);
+	gaim_conv_im_start_send_typed_timeout(im);
+
+	/* Check if we need to send another GAIM_TYPING message */
 	if (first || (gaim_conv_im_get_type_again(im) != 0 &&
-				  time(NULL) > gaim_conv_im_get_type_again(im))) {
-
-		int timeout = serv_send_typing(gaim_conversation_get_gc(conv),
-									   (char *)gaim_conversation_get_name(conv),
-									   GAIM_TYPING);
-
-		if (timeout)
-			gaim_conv_im_set_type_again(im, time(NULL) + timeout);
-		else
-			gaim_conv_im_set_type_again(im, 0);
+				  time(NULL) > gaim_conv_im_get_type_again(im)))
+	{
+		unsigned int timeout;
+		timeout = serv_send_typing(gaim_conversation_get_gc(conv),
+								   gaim_conversation_get_name(conv),
+								   GAIM_TYPING);
+		gaim_conv_im_set_type_again(im, timeout);
 	}
 }
 
--- a/src/gtkpounce.c	Tue Jun 20 04:05:56 2006 +0000
+++ b/src/gtkpounce.c	Tue Jun 20 08:17:49 2006 +0000
@@ -81,6 +81,7 @@
 	GtkWidget *idle;
 	GtkWidget *idle_return;
 	GtkWidget *typing;
+	GtkWidget *typed;
 	GtkWidget *stop_typing;
 	GtkWidget *message_recv;
 
@@ -278,6 +279,9 @@
 	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->typing)))
 		events |= GAIM_POUNCE_TYPING;
 
+	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->typed)))
+		events |= GAIM_POUNCE_TYPED;
+
 	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->stop_typing)))
 		events |= GAIM_POUNCE_TYPING_STOPPED;
 
@@ -564,7 +568,7 @@
 	/* Create the "Pounce When Buddy..." frame. */
 	frame = gaim_gtk_make_frame(vbox2, _("Pounce When Buddy..."));
 
-	table = gtk_table_new(2, 4, FALSE);
+	table = gtk_table_new(5, 2, FALSE);
 	gtk_container_add(GTK_CONTAINER(frame), table);
 	gtk_table_set_col_spacings(GTK_TABLE(table), GAIM_HIG_BORDER);
 	gtk_widget_show(table);
@@ -583,28 +587,32 @@
 		gtk_check_button_new_with_mnemonic(_("Is no longer i_dle"));
 	dialog->typing =
 		gtk_check_button_new_with_mnemonic(_("Starts _typing"));
+	dialog->typed =
+		gtk_check_button_new_with_mnemonic(_("P_auses while typing"));
 	dialog->stop_typing =
 		gtk_check_button_new_with_mnemonic(_("Stops t_yping"));
 	dialog->message_recv =
 		gtk_check_button_new_with_mnemonic(_("Sends a _message"));
 
-	gtk_table_attach(GTK_TABLE(table), dialog->signon,       0, 1, 0, 1,
+	gtk_table_attach(GTK_TABLE(table), dialog->message_recv, 0, 1, 0, 1,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->signoff,      1, 2, 0, 1,
+	gtk_table_attach(GTK_TABLE(table), dialog->signon,       0, 1, 1, 2,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->away,         0, 1, 1, 2,
+	gtk_table_attach(GTK_TABLE(table), dialog->signoff,      0, 1, 2, 3,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->away_return,  1, 2, 1, 2,
+	gtk_table_attach(GTK_TABLE(table), dialog->away,         0, 1, 3, 4,
+					 GTK_FILL, 0, 0, 0);
+	gtk_table_attach(GTK_TABLE(table), dialog->away_return,  0, 1, 4, 5,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->idle,         0, 1, 2, 3,
+	gtk_table_attach(GTK_TABLE(table), dialog->idle,         1, 2, 0, 1,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->idle_return,  1, 2, 2, 3,
+	gtk_table_attach(GTK_TABLE(table), dialog->idle_return,  1, 2, 1, 2,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->typing,       0, 1, 3, 4,
+	gtk_table_attach(GTK_TABLE(table), dialog->typing,       1, 2, 2, 3,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->stop_typing,  1, 2, 3, 4,
+	gtk_table_attach(GTK_TABLE(table), dialog->typed,        1, 2, 3, 4,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->message_recv, 0, 1, 4, 5,
+	gtk_table_attach(GTK_TABLE(table), dialog->stop_typing,  1, 2, 4, 5,
 					 GTK_FILL, 0, 0, 0);
 
 	gtk_widget_show(dialog->signon);
@@ -614,6 +622,7 @@
 	gtk_widget_show(dialog->idle);
 	gtk_widget_show(dialog->idle_return);
 	gtk_widget_show(dialog->typing);
+	gtk_widget_show(dialog->typed);
 	gtk_widget_show(dialog->stop_typing);
 	gtk_widget_show(dialog->message_recv);
 
@@ -845,6 +854,8 @@
 									(events & GAIM_POUNCE_IDLE_RETURN));
 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->typing),
 									(events & GAIM_POUNCE_TYPING));
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->typed),
+									(events & GAIM_POUNCE_TYPED));
 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->stop_typing),
 									(events & GAIM_POUNCE_TYPING_STOPPED));
 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->message_recv),
@@ -1429,6 +1440,8 @@
 		tmp = g_strdup_printf(
 				   (events & GAIM_POUNCE_TYPING) ?
 				   _("%s has started typing to you (%s)") :
+				   (events & GAIM_POUNCE_TYPED) ?
+				   _("%s has paused while typing to you (%s)") :
 				   (events & GAIM_POUNCE_SIGNON) ?
 				   _("%s has signed on (%s)") :
 				   (events & GAIM_POUNCE_IDLE_RETURN) ?
--- a/src/pounce.c	Tue Jun 20 04:05:56 2006 +0000
+++ b/src/pounce.c	Tue Jun 20 08:17:49 2006 +0000
@@ -206,6 +206,8 @@
 		add_event_to_xmlnode(child, "return-from-idle");
 	if (events & GAIM_POUNCE_TYPING)
 		add_event_to_xmlnode(child, "start-typing");
+	if (events & GAIM_POUNCE_TYPED)
+		add_event_to_xmlnode(child, "typed");
 	if (events & GAIM_POUNCE_TYPING_STOPPED)
 		add_event_to_xmlnode(child, "stop-typing");
 	if (events & GAIM_POUNCE_MESSAGE_RECEIVED)
@@ -411,7 +413,7 @@
 	else if (!strcmp(element_name, "option")) {
 		if (!strcmp(data->option_type, "on-away"))
 			data->options |= GAIM_POUNCE_OPTION_AWAY;
-		
+
 		g_free(data->option_type);
 		data->option_type = NULL;
 	}
@@ -430,6 +432,8 @@
 			data->events |= GAIM_POUNCE_IDLE_RETURN;
 		else if (!strcmp(data->event_type, "start-typing"))
 			data->events |= GAIM_POUNCE_TYPING;
+		else if (!strcmp(data->event_type, "typed"))
+			data->events |= GAIM_POUNCE_TYPED;
 		else if (!strcmp(data->event_type, "stop-typing"))
 			data->events |= GAIM_POUNCE_TYPING_STOPPED;
 		else if (!strcmp(data->event_type, "message-received"))
@@ -1059,10 +1063,16 @@
 	conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, name, account);
 	if (conv != NULL)
 	{
+		GaimTypingState state;
 		GaimPounceEvent event;
 
-		event = (gaim_conv_im_get_typing_state(GAIM_CONV_IM(conv)) == GAIM_TYPING
-				 ? GAIM_POUNCE_TYPING : GAIM_POUNCE_TYPING_STOPPED);
+		state = gaim_conv_im_get_typing_state(GAIM_CONV_IM(conv));
+		if (state == GAIM_TYPED)
+			event = GAIM_POUNCE_TYPED;
+		else if (state == GAIM_NOT_TYPING)
+			event = GAIM_POUNCE_TYPING_STOPPED;
+		else
+			event = GAIM_POUNCE_TYPING;
 
 		gaim_pounce_execute(account, name, event);
 	}
@@ -1105,6 +1115,8 @@
 
 	gaim_signal_connect(conv_handle, "buddy-typing",
 						handle, GAIM_CALLBACK(buddy_typing_cb), NULL);
+	gaim_signal_connect(conv_handle, "buddy-typed",
+						handle, GAIM_CALLBACK(buddy_typing_cb), NULL);
 	gaim_signal_connect(conv_handle, "buddy-typing-stopped",
 						handle, GAIM_CALLBACK(buddy_typing_cb), NULL);
 
--- a/src/pounce.h	Tue Jun 20 04:05:56 2006 +0000
+++ b/src/pounce.h	Tue Jun 20 08:17:49 2006 +0000
@@ -35,16 +35,17 @@
  */
 typedef enum
 {
-	GAIM_POUNCE_NONE           = 0x00, /**< No events.                    */
-	GAIM_POUNCE_SIGNON         = 0x01, /**< The buddy signed on.          */
-	GAIM_POUNCE_SIGNOFF        = 0x02, /**< The buddy signed off.         */
-	GAIM_POUNCE_AWAY           = 0x04, /**< The buddy went away.          */
-	GAIM_POUNCE_AWAY_RETURN    = 0x08, /**< The buddy returned from away. */
-	GAIM_POUNCE_IDLE           = 0x10, /**< The buddy became idle.        */
-	GAIM_POUNCE_IDLE_RETURN    = 0x20, /**< The buddy is no longer idle.  */
-	GAIM_POUNCE_TYPING         = 0x40, /**< The buddy started typing.     */
-	GAIM_POUNCE_TYPING_STOPPED = 0x80, /**< The buddy stopped typing.     */
-	GAIM_POUNCE_MESSAGE_RECEIVED = 0x100 /**< The buddy sent a message    */
+	GAIM_POUNCE_NONE             = 0x000, /**< No events.                    */
+	GAIM_POUNCE_SIGNON           = 0x001, /**< The buddy signed on.          */
+	GAIM_POUNCE_SIGNOFF          = 0x002, /**< The buddy signed off.         */
+	GAIM_POUNCE_AWAY             = 0x004, /**< The buddy went away.          */
+	GAIM_POUNCE_AWAY_RETURN      = 0x008, /**< The buddy returned from away. */
+	GAIM_POUNCE_IDLE             = 0x010, /**< The buddy became idle.        */
+	GAIM_POUNCE_IDLE_RETURN      = 0x020, /**< The buddy is no longer idle.  */
+	GAIM_POUNCE_TYPING           = 0x040, /**< The buddy started typing.     */
+	GAIM_POUNCE_TYPED            = 0x080, /**< The buddy has entered text.   */
+	GAIM_POUNCE_TYPING_STOPPED   = 0x100, /**< The buddy stopped typing.     */
+	GAIM_POUNCE_MESSAGE_RECEIVED = 0x200  /**< The buddy sent a message      */
 
 } GaimPounceEvent;
 
--- a/src/protocols/jabber/message.c	Tue Jun 20 04:05:56 2006 +0000
+++ b/src/protocols/jabber/message.c	Tue Jun 20 08:17:49 2006 +0000
@@ -589,7 +589,7 @@
 	return 1;
 }
 
-int jabber_send_typing(GaimConnection *gc, const char *who, int typing)
+unsigned int jabber_send_typing(GaimConnection *gc, const char *who, GaimTypingState state)
 {
 	JabberMessage *jm;
 	JabberBuddy *jb;
@@ -611,9 +611,9 @@
 	jm->to = g_strdup(who);
 	jm->id = jabber_get_next_id(jm->js);
 
-	if(GAIM_TYPING == typing)
+	if(GAIM_TYPING == state)
 		jm->chat_state = JM_STATE_COMPOSING;
-	else if(GAIM_TYPED == typing)
+	else if(GAIM_TYPED == state)
 		jm->chat_state = JM_STATE_PAUSED;
 	else
 		jm->chat_state = JM_STATE_ACTIVE;
--- a/src/protocols/jabber/message.h	Tue Jun 20 04:05:56 2006 +0000
+++ b/src/protocols/jabber/message.h	Tue Jun 20 08:17:49 2006 +0000
@@ -71,7 +71,7 @@
 		GaimMessageFlags flags);
 int jabber_message_send_chat(GaimConnection *gc, int id, const char *message, GaimMessageFlags flags);
 
-int jabber_send_typing(GaimConnection *gc, const char *who, int typing);
+unsigned int jabber_send_typing(GaimConnection *gc, const char *who, GaimTypingState state);
 
 
 #endif /* _GAIM_JABBER_MESSAGE_H_ */
--- a/src/protocols/msn/msn.c	Tue Jun 20 04:05:56 2006 +0000
+++ b/src/protocols/msn/msn.c	Tue Jun 20 08:17:49 2006 +0000
@@ -836,8 +836,8 @@
 	return 1;
 }
 
-static int
-msn_send_typing(GaimConnection *gc, const char *who, int typing)
+static unsigned int
+msn_send_typing(GaimConnection *gc, const char *who, GaimTypingState state)
 {
 	GaimAccount *account;
 	MsnSession *session;
@@ -847,7 +847,12 @@
 	account = gaim_connection_get_account(gc);
 	session = gc->proto_data;
 
-	if (!typing)
+	/*
+	 * TODO: I feel like this should be "if (state != GAIM_TYPING)"
+	 *       but this is how it was before, and I don't want to break
+	 *       anything. --KingAnt
+	 */
+	if (state == GAIM_NOT_TYPING)
 		return 0;
 
 	if (!g_ascii_strcasecmp(who, gaim_account_get_username(account)))
--- a/src/protocols/novell/novell.c	Tue Jun 20 04:05:56 2006 +0000
+++ b/src/protocols/novell/novell.c	Tue Jun 20 08:17:49 2006 +0000
@@ -2303,8 +2303,8 @@
 	return 1;
 }
 
-static int
-novell_send_typing(GaimConnection * gc, const char *name, int typing)
+static unsigned int
+novell_send_typing(GaimConnection * gc, const char *name, GaimTypingState state)
 {
 	NMConference *conf = NULL;
 	NMUser *user;
@@ -2312,11 +2312,11 @@
 	NMERR_T rc = NM_OK;
 
 	if (gc == NULL || name == NULL)
-		return -1;
+		return 0;
 
 	user = gc->proto_data;
 	if (user == NULL)
-		return -1;
+		return 0;
 
 	/* Need to get the DN for the buddy so we can look up the convo */
 	dn = nm_lookup_dn(user, name);
@@ -2327,7 +2327,7 @@
 		if (conf) {
 
 			rc = nm_send_typing(user, conf,
-								((typing == GAIM_TYPING) ? TRUE : FALSE), NULL);
+								((state == GAIM_TYPING) ? TRUE : FALSE), NULL);
 			_check_for_disconnect(user, rc);
 
 		}
--- a/src/protocols/oscar/oscar.c	Tue Jun 20 04:05:56 2006 +0000
+++ b/src/protocols/oscar/oscar.c	Tue Jun 20 08:17:49 2006 +0000
@@ -3945,8 +3945,8 @@
 		flap_connection_send_keepalive(od, conn);
 }
 
-static int
-oscar_send_typing(GaimConnection *gc, const char *name, int typing)
+static unsigned int
+oscar_send_typing(GaimConnection *gc, const char *name, GaimTypingState state)
 {
 	OscarData *od;
 	PeerConnection *conn;
@@ -3956,7 +3956,7 @@
 
 	if ((conn != NULL) && (conn->ready))
 	{
-		peer_odc_send_typing(conn, typing);
+		peer_odc_send_typing(conn, state);
 	}
 	else {
 		/* Don't send if this turkey is in our deny list */
@@ -3965,9 +3965,9 @@
 		if (!list) {
 			struct buddyinfo *bi = g_hash_table_lookup(od->buddyinfo, gaim_normalize(gc->account, name));
 			if (bi && bi->typingnot) {
-				if (typing == GAIM_TYPING)
+				if (state == GAIM_TYPING)
 					aim_im_sendmtn(od, 0x0001, name, 0x0002);
-				else if (typing == GAIM_TYPED)
+				else if (state == GAIM_TYPED)
 					aim_im_sendmtn(od, 0x0001, name, 0x0001);
 				else
 					aim_im_sendmtn(od, 0x0001, name, 0x0000);
--- a/src/protocols/sametime/sametime.c	Tue Jun 20 04:05:56 2006 +0000
+++ b/src/protocols/sametime/sametime.c	Tue Jun 20 08:17:49 2006 +0000
@@ -3965,14 +3965,14 @@
 }
 
 
-static int mw_prpl_send_typing(GaimConnection *gc, const char *name,
-			       int typing) {
+static unsigned int mw_prpl_send_typing(GaimConnection *gc, const char *name,
+			       GaimTypingState state) {
   
   struct mwGaimPluginData *pd;
   struct mwIdBlock who = { (char *) name, NULL };
   struct mwConversation *conv;
 
-  gpointer t = GINT_TO_POINTER(!! typing);
+  gpointer t = GINT_TO_POINTER(!! state);
 
   g_return_val_if_fail(gc != NULL, 0);
   pd = gc->proto_data;
@@ -3984,7 +3984,7 @@
   if(mwConversation_isOpen(conv))
     return ! mwConversation_send(conv, mwImSend_TYPING, t);
 
-  if(typing) {
+  if ((state == GAIM_TYPING) || (state == GAIM_TYPED)) {
     /* let's only open a channel for typing, not for not-typing.
        Otherwise two users in psychic mode will continually open
        conversations to each other, never able to get rid of them, as
@@ -3996,6 +3996,11 @@
       mwConversation_open(conv);
   }
 
+  /*
+   * TODO: This should probably be "0."  When it's set to 1, the Gaim
+   *       core will call serv_send_typing(gc, who, GAIM_TYPING) once
+   *       every second until the Gaim user stops typing. --KingAnt
+   */
   return 1;
 }
 
--- a/src/protocols/simple/simple.c	Tue Jun 20 04:05:56 2006 +0000
+++ b/src/protocols/simple/simple.c	Tue Jun 20 08:17:49 2006 +0000
@@ -1053,7 +1053,7 @@
 	send_sip_response(sip->gc, msg, 200, "OK", NULL);
 }
 
-static int simple_typing(GaimConnection *gc, const char *name, int typing) {
+static unsigned int simple_typing(GaimConnection *gc, const char *name, GaimTypingState state) {
 	struct simple_account_data *sip = gc->proto_data;
 
 	gchar *xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
@@ -1065,16 +1065,22 @@
 			"<refresh>60</refresh>\n"
 			"</isComposing>";
 	gchar *recv = g_strdup(name);
-	if(typing == GAIM_TYPING) {
+	if(state == GAIM_TYPING) {
 		gchar *msg = g_strdup_printf(xml, "active");
 		simple_send_message(sip, recv, msg, "application/im-iscomposing+xml");
 		g_free(msg);
-	} else {
+	} else /* TODO: Only if (state == GAIM_TYPED) ? */ {
 		gchar *msg = g_strdup_printf(xml, "idle");
 		simple_send_message(sip, recv, msg, "application/im-iscomposing+xml");
 		g_free(msg);
 	}
 	g_free(recv);
+	/*
+	 * TODO: Is this right?  It will cause the core to call
+	 *       serv_send_typing(gc, who, GAIM_TYPING) once every second
+	 *       until the user stops typing.  If that's not desired,
+	 *       then return 0 instead.
+	 */
 	return 1;
 }
 
--- a/src/protocols/yahoo/yahoo.c	Tue Jun 20 04:05:56 2006 +0000
+++ b/src/protocols/yahoo/yahoo.c	Tue Jun 20 08:17:49 2006 +0000
@@ -3131,12 +3131,12 @@
 	return ret;
 }
 
-static int yahoo_send_typing(GaimConnection *gc, const char *who, int typ)
+static unsigned int yahoo_send_typing(GaimConnection *gc, const char *who, GaimTypingState state)
 {
 	struct yahoo_data *yd = gc->proto_data;
 	struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_TYPING, 0);
 	yahoo_packet_hash(pkt, "ssssss", 49, "TYPING", 1, gaim_connection_get_display_name(gc),
-	                  14, " ", 13, typ == GAIM_TYPING ? "1" : "0",
+	                  14, " ", 13, state == GAIM_TYPING ? "1" : "0",
 	                  5, who, 1002, "1");
 
 	yahoo_packet_send_and_free(pkt, yd);
--- a/src/protocols/zephyr/zephyr.c	Tue Jun 20 04:05:56 2006 +0000
+++ b/src/protocols/zephyr/zephyr.c	Tue Jun 20 08:17:49 2006 +0000
@@ -2450,14 +2450,15 @@
 	return "zephyr";
 }
 
-static int zephyr_send_typing(GaimConnection *gc, const char *who, int typing) {
+static unsigned int zephyr_send_typing(GaimConnection *gc, const char *who, GaimTypingState state) {
 	gchar *recipient;
 	zephyr_account *zephyr = gc->proto_data;
 	if (use_tzc(zephyr)) 
 		return 0;
 
-	if (!typing)
+	if (state == GAIM_NOT_TYPING)
 		return 0;
+
 	/* XXX We probably should care if this fails. Or maybe we don't want to */
 	if (!who) {
 		gaim_debug_info("zephyr", "who is null\n");
@@ -2476,6 +2477,12 @@
 	gaim_debug_info("zephyr","about to send typing notification to %s\n",recipient);
 	zephyr_send_message(zephyr,"MESSAGE","PERSONAL",recipient,"","","PING");
 	gaim_debug_info("zephyr","sent typing notification\n");
+
+	/*
+	 * TODO: Is this correct?  It means we will call
+	 *       serv_send_typing(gc, who, GAIM_TYPING) once every 15 seconds
+	 *       until the Gaim user stops typing.
+	 */
 	return ZEPHYR_TYPING_SEND_TIMEOUT;
 }
 
--- a/src/prpl.h	Tue Jun 20 04:05:56 2006 +0000
+++ b/src/prpl.h	Tue Jun 20 08:17:49 2006 +0000
@@ -224,7 +224,7 @@
 					GaimMessageFlags flags);
 
 	void (*set_info)(GaimConnection *, const char *info);
-	int  (*send_typing)(GaimConnection *, const char *name, int typing);
+	unsigned int (*send_typing)(GaimConnection *, const char *name, GaimTypingState state);
 	void (*get_info)(GaimConnection *, const char *who);
 	void (*set_status)(GaimAccount *account, GaimStatus *status);
 
--- a/src/server.c	Tue Jun 20 04:05:56 2006 +0000
+++ b/src/server.c	Tue Jun 20 08:17:49 2006 +0000
@@ -38,18 +38,16 @@
 #define SECS_BEFORE_RESENDING_AUTORESPONSE 600
 #define SEX_BEFORE_RESENDING_AUTORESPONSE "Only after you're married"
 
-/* This should return the elapsed time in seconds in which Gaim will not send
- * typing notifications.
- * if it returns zero, it will not send any more typing notifications
- * typing is a flag - TRUE for typing, FALSE for stopped typing */
-int serv_send_typing(GaimConnection *g, const char *name, int typing) {
+unsigned int
+serv_send_typing(GaimConnection *gc, const char *name, GaimTypingState state)
+{
 	GaimPluginProtocolInfo *prpl_info = NULL;
 
-	if (g != NULL && g->prpl != NULL)
-		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+	if (gc != NULL && gc->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
 
-	if (g && prpl_info && prpl_info->send_typing)
-		return prpl_info->send_typing(g, name, typing);
+	if (prpl_info && prpl_info->send_typing)
+		return prpl_info->send_typing(gc, name, state);
 
 	return 0;
 }
@@ -150,8 +148,8 @@
 		lar->sent = time(NULL);
 	}
 
-	if (conv && gaim_conv_im_get_type_again_timeout(GAIM_CONV_IM(conv)))
-		gaim_conv_im_stop_type_again_timeout(GAIM_CONV_IM(conv));
+	if (conv && gaim_conv_im_get_send_typed_timeout(GAIM_CONV_IM(conv)))
+		gaim_conv_im_stop_send_typed_timeout(GAIM_CONV_IM(conv));
 
 	return val;
 }
@@ -593,7 +591,7 @@
 		else
 		{
 			gaim_signal_emit(gaim_conversations_get_handle(),
-							 "buddy-typing-stopped", gc->account, name);
+							 "buddy-typed", gc->account, name);
 		}
 	}
 
@@ -618,6 +616,11 @@
 		gaim_conv_im_set_typing_state(im, GAIM_NOT_TYPING);
 		gaim_conv_im_update_typing(im);
 	}
+	else
+	{
+		gaim_signal_emit(gaim_conversations_get_handle(),
+						 "buddy-typing-stopped", gc->account, name);
+	}
 
 	gaim_signal_emit(gaim_conversations_get_handle(),
 					 "buddy-typing-stopped", gc->account, name);
--- a/src/server.h	Tue Jun 20 04:05:56 2006 +0000
+++ b/src/server.h	Tue Jun 20 08:17:49 2006 +0000
@@ -33,11 +33,27 @@
 extern "C" {
 #endif
 
+/**
+ * Send a typing message to a given user over a given connection.
+ *
+ * TODO: Could probably move this into the conversation API.
+ *
+ * @param typing One of GAIM_TYPING, GAIM_TYPED, or GAIM_NOT_TYPING.
+ * @return A quiet-period, specified in seconds, where Gaim will not
+ *         send any additional typing notification messages.  Most
+ *         protocols should return 0, which means that no additional
+ *         GAIM_TYPING messages need to be sent.  If this is 5, for
+ *         example, then Gaim will wait five seconds, and if the Gaim
+ *         user is still typing then Gaim will send another GAIM_TYPING
+ *         message.
+ */
+unsigned int serv_send_typing(GaimConnection *gc, const char *name, GaimTypingState state);
+
+void serv_move_buddy(GaimBuddy *, GaimGroup *, GaimGroup *);
 int  serv_send_im(GaimConnection *, const char *, const char *, GaimMessageFlags flags);
 void serv_get_info(GaimConnection *, const char *);
 void serv_set_info(GaimConnection *, const char *);
-int  serv_send_typing(GaimConnection *, const char *, int);
-void serv_move_buddy(GaimBuddy *, GaimGroup *, GaimGroup *);
+
 void serv_add_permit(GaimConnection *, const char *);
 void serv_add_deny(GaimConnection *, const char *);
 void serv_rem_permit(GaimConnection *, const char *);
@@ -51,12 +67,31 @@
 int  serv_chat_send(GaimConnection *, int, const char *, GaimMessageFlags flags);
 void serv_alias_buddy(GaimBuddy *);
 void serv_got_alias(GaimConnection *gc, const char *who, const char *alias);
+
+/**
+ * Receive a typing message from a remote user.  Either GAIM_TYPING
+ * or GAIM_TYPED.  If the user has stopped typing then use
+ * serv_got_typing_stopped instead.
+ *
+ * TODO: Could probably move this into the conversation API.
+ *
+ * @param timeout If this is a number greater than 0, then
+ *        Gaim will wait this number of seconds and then
+ *        set this buddy to the GAIM_NOT_TYPING state.  This
+ *        is used by protocols that send repeated typing messages
+ *        while the user is composing the message.
+ */
 void serv_got_typing(GaimConnection *gc, const char *name, int timeout,
 					 GaimTypingState state);
-void serv_set_buddyicon(GaimConnection *gc, const char *filename);
+
+/**
+ * TODO: Could probably move this into the conversation API.
+ */
 void serv_got_typing_stopped(GaimConnection *gc, const char *name);
+
 void serv_got_im(GaimConnection *gc, const char *who, const char *msg,
 				 GaimMessageFlags flags, time_t mtime);
+void serv_set_buddyicon(GaimConnection *gc, const char *filename);
 void serv_got_chat_invite(GaimConnection *gc, const char *name,
 						  const char *who, const char *message,
 						  GHashTable *data);