pidgin/pidgin

2bb66ef1475e
Fix a crash when hovering over an exceptionally long URL (4074 characters,
but it might be different for you) in a gtkimhtml widget (such as the IM
window).

This was reported on the support mailing list:
http://pidgin.im/pipermail/support/2013-March/012980.html
http://pidgin.im/pipermail/support/2013-March/012981.html

Repro steps:
1. "gdb pidgin"

2. "break gdk_x_error"
You'll probably need to type "y" to make breakpoint pending on future shared library load. (Or you could run first then add the breakpoint.)

3. "run --sync"
--sync probably isn't actually necessary. Theoretically it should make the backtrace more useful. Like, maybe it could actually show the Pidgin calls that trigger the problem? Or maybe the problem call starts from the gtk main loop, so there would never be Pidgin code in the backtrace? It's also possible we don't pass our args to the right places to make --sync work.

4. "cont"
I hit the breakpoint at startup for some reason. I've just been continuing past it. Doesn't seem to affect anything.

5. Open an IM window.

6. Paste a long URL.

7. Mouse over the long URL.

8. The breakpoint is hit.

9. "bt"

#0 gdk_x_error (display=0x740e30, error=0x7fffffffbc20)
at /build/buildd/gtk+2.0-2.24.13/gdk/x11/gdkmain-x11.c:458
#1 0x00007ffff73b94f6 in _XError () from /usr/lib/x86_64-linux-gnu/libX11.so.6
#2 0x00007ffff73b6741 in ?? () from /usr/lib/x86_64-linux-gnu/libX11.so.6
#3 0x00007ffff73b6785 in ?? () from /usr/lib/x86_64-linux-gnu/libX11.so.6
#4 0x00007ffff73b7378 in _XReply () from /usr/lib/x86_64-linux-gnu/libX11.so.6
#5 0x00007ffff73b2d3d in XSync () from /usr/lib/x86_64-linux-gnu/libX11.so.6
#6 0x00007ffff73b2dcb in ?? () from /usr/lib/x86_64-linux-gnu/libX11.so.6
#7 0x00007ffff73b9e2f in ?? () from /usr/lib/x86_64-linux-gnu/libX11.so.6
#8 0x00007ffff7395d14 in XCreatePixmap () from /usr/lib/x86_64-linux-gnu/libX11.so.6
#9 0x00007ffff62bd0d2 in _gdk_pixmap_new (drawable=0x186ec60, width=width@entry=32774, height=25, depth=24,
depth@entry=-1) at /build/buildd/gtk+2.0-2.24.13/gdk/x11/gdkpixmap-x11.c:175
#10 0x00007ffff6289617 in IA__gdk_pixmap_new (drawable=drawable@entry=0x186ec60, width=width@entry=32774,
height=, depth=depth@entry=-1) at /build/buildd/gtk+2.0-2.24.13/gdk/gdkpixmap.c:249
#11 0x00007ffff6297036 in gdk_window_begin_implicit_paint (rect=0x7fffffffbed0, window=0x186ec60)
at /build/buildd/gtk+2.0-2.24.13/gdk/gdkwindow.c:2779
#12 gdk_window_process_updates_internal (window=0x186ec60) at /build/buildd/gtk+2.0-2.24.13/gdk/gdkwindow.c:5574
#13 0x00007ffff6299201 in IA__gdk_window_process_all_updates ()
at /build/buildd/gtk+2.0-2.24.13/gdk/gdkwindow.c:5696
#14 0x00007ffff6299269 in gdk_window_update_idle (data=)
at /build/buildd/gtk+2.0-2.24.13/gdk/gdkwindow.c:5322
#15 0x00007ffff6276e77 in gdk_threads_dispatch (data=0x1a45c40) at /build/buildd/gtk+2.0-2.24.13/gdk/gdk.c:512
#16 0x00007ffff53d8ab5 in g_main_context_dispatch () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#17 0x00007ffff53d8de8 in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#18 0x00007ffff53d91e2 in g_main_loop_run () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#19 0x00007ffff663ec77 in IA__gtk_main () at /build/buildd/gtk+2.0-2.24.13/gtk/gtkmain.c:1271
#20 0x0000000000499950 in main (argc=1, argv=0x7fffffffe538) at gtkmain.c:933

This is the message it prints when it exits:
The program 'Pidgin' received an X Window System error.
This probably reflects a bug in the program.
The error was 'BadAlloc (insufficient resources for operation)'.
(Details: serial 14454 error_code 11 request_code 53 minor_code 0)
(Note to programmers: normally, X errors are reported asynchronously;
that is, you will receive the error a while after causing it.
To debug your program, run it with the --sync command line
option to change this behavior. You can then get a meaningful
backtrace from your debugger if you break on the gdk_x_error() function.)

I think it's kinda lame that gdk/gtk/xlib/whatever barfs when we give it a
ridiculously wide pango layout, but whatever. I'm assuming this problem is
gtkimhtml specific, and so I don't want to spend a whole lot of time fixing
it. As an easy fix I want to just truncate the URL displayed in the tooltip
to 200 characters.

Here's an example URL that I used to trigger the crash:
http://www.example.com/?%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%30123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789%3012345678901234567890123456789012345678901234567890123456789012345678
/** @page signal-howto Signals HOWTO
@section Introduction
The libpurple signals interface is used for general event notification, such
as plugins being loaded or unloaded, allowing the GUI frontend to respond
appropriately to changing internal data. Unfortunately, its use is not at all
obvious from the information in the header files. This document uses code
snippets from the Pidgin/libpurple plugin systems to illustrate the proper
use of signals.
@section overview Overview of Signals
Signals in libpurple are very similar to those in GTK+. When certain events
happen, a named signal is "emitted" from a certain object. Emitting the
signal triggers a series of callbacks that have been "connected" to that
signal for that object. These callbacks take appropriate action in response
to the signal.
@section registering_signal Registering a Signal
The first step of using a signal is registering it with libpurple so that
callbacks may be connected to it. This is done using purple_signal_register()
Here is a slightly modified example from @c purple_plugins_init in
@c libpurple/plugin.c :
@code
purple_signal_register( purple_plugins_get_handle(), /* Instance */
"plugin-load", /* Signal name */
purple_marshal_VOID__POINTER,/* Marshal function */
NULL, /* Callback return value type */
1, /* Number of callback arguments (not including void *data) */
purple_value_new(PURPLE_TYPE_SUBTYPE,PURPLE_SUBTYPE_PLUGIN) /* Type of first callback argument */
);
@endcode
@subsection Instance
A reference to the object from which this signal is emitted, and to which
potential callbacks should be connected. In this case, it will be the entire
plugin module emitting the signal.
@subsection signalname Signal Name
Unique identifier for the signal itself.
@subsection therest Callback function definition
The rest of the arguments specify the form of the callback function.
@subsubsection marshalfunc Marshal Function
@c purple_marshal_VOID__POINTER represents the callback function prototype,
not including a "data" argument, explained later. The form is
@c purple_marshal_RETURNVALUETYPE__ARG1TYPE_ARG2TYPE_ETC. See signals.h for
more possible types.
In this case, the callback will have the form
@code
void cb(void *arg1, void *data)
@endcode
If @c purple_marshal_BOOLEAN__POINTER_POINTER_POINTER were specified, it
would be:
@code
gboolean cb(void *arg1, void *arg2, void *arg3, void *data)
@endcode
The @c void @c *data argument at the end of each callback function
provides the data argument given to purple_signal_connect() .
@subsubsection cb_ret_type Callback return value type
In our case, this is NULL, meaning "returns void".
@todo This could be described better.
@subsubsection num_args Number of arguments
The number of arguments (not including @c data ) that the callback function
will take.
@subsubsection type_arg Type of argument
@c purple_value_new(PURPLE_TYPE_SUBTYPE,PURPLE_SUBTYPE_PLUGIN) specifies that
the first argument given to the callback will be a @c PurplePlugin* . You
will need as many "type of argument" arguments to purple_signal_register() as
you specified in "Number of arguments" above.
@todo Describe this more.
@see value.h
@section connect Connecting to the signal
Once the signal is registered, you can connect callbacks to it. First, you
must define a callback function, such as this one from gtkplugin.c :
@code
static void plugin_load_cb(PurplePlugin *plugin, gpointer data)
{
GtkTreeView *view = (GtkTreeView *)data;
plugin_loading_common(plugin, view, TRUE);
}
@endcode
Note that the callback function prototype matches that specified in the call
to purple_signal_register() above.
Once the callback function is defined, you can connect it to the signal.
Again from gtkplugin.c , in @c pidgin_plugin_dialog_show() :
@code
purple_signal_connect(purple_plugins_get_handle(), "plugin-load", /* What to connect to */
plugin_dialog, /* Object receiving the signal */
PURPLE_CALLBACK(plugin_load_cb), /* Callback function */
event_view, /* Data to pass to the callback function
);
@endcode
The first two arguments ("What to connect to") specify the object emitting
the signal (the plugin module) and what signal to listen for ("plugin-load").
The object receiving the signal is @c plugin_dialog , the Pidgin plugins
dialog. When @c plugin_dialog is deleted, then
@c purple_signals_disconnect_by_handle(plugin_dialog) should be called to
remove all signal connections it is associated with.
The callback function is given using a helper macro, and finally the
@c data argument to be passed to @c plugin_load_cb is given as @c event_view,
a pointer to the GTK widget that @c plugin_load_cb needs to update.
@section emit-signal Emitting a signal
Connecting callbacks to signals is all well and good, but how do you "fire"
the signal and trigger the callback? At some point, you must "emit" the
signal, which immediately calls all connected callbacks.
As seen in @c purple_plugin_load() in plugin.c :
@code
purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin);
@endcode
This causes the signal "plugin-load" to be emitted from the plugin module
(given by @c purple_plugins_get_handle() ), with the newly loaded plugin as
the argument to pass to any registered callback functions.
In our example, @c plugin_load_cb is called immediately as
@code
plugin_load_cb(plugin, event_view);
@endcode
and does whatever it does.
*/