gdb/python: new selected_context event

This commit introduces a new Python event, selected_context.  This
event is attached to the user_selected_context_changed observer, which
triggers when the user changes the currently selected inferior,
thread, or frame.

Adding this event allows a Python extension to update in response to
user driven changes without having to poll the state from a
before_prompt hook, which is what I currently do to achieve the same
results.

I did consider splitting the user_selected_context_changed observer
into 3 separate Python events, inferior_changed, thread_changed, and
frame_changed, but I couldn't see any significant advantage to doing
this, so in the end I went with just a single event, and the event
object contains the inferior, thread, and frame.

Additionally, the user isn't informed about which aspect of the
context changed.  That is, every event carries the inferior, thread,
and frame, so an event triggered when switching frames will looks
identical to an event triggered when switching inferiors.  If the user
wants to know what changed then they will have to track the current
state themselves, and then compare the event state to the stored
current state.  In many cases though I suspect that just being told
something changed, and then updating everything will be sufficient,
which is why I've not bothered trying to inform the user what changed.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=24482

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Approved-By: Tom Tromey <tom@tromey.com>
This commit is contained in:
Andrew Burgess 2026-02-22 11:16:15 +00:00
parent 602c91f4d1
commit ee89a3c9ef
8 changed files with 347 additions and 0 deletions

View file

@ -46,3 +46,4 @@ GDB_PY_DEFINE_EVENT(executable_changed)
GDB_PY_DEFINE_EVENT(new_progspace)
GDB_PY_DEFINE_EVENT(free_progspace)
GDB_PY_DEFINE_EVENT(tui_enabled)
GDB_PY_DEFINE_EVENT(selected_context)

View file

@ -145,3 +145,8 @@ GDB_PY_DEFINE_EVENT_TYPE (tui_enabled,
"TuiEnabledEvent",
"GDB TUI enabled event object",
event_object_type);
GDB_PY_DEFINE_EVENT_TYPE (selected_context,
"SelectedContextEvent",
"GDB user selected context event object",
event_object_type);

View file

@ -997,6 +997,58 @@ gdbpy_selected_inferior (PyObject *self, PyObject *args)
inferior_to_inferior_object (current_inferior ()).release ());
}
/* Implement the selected_context event handler. This is called when some
aspect of the inferior's context (inferior, thread, or frame) is
changed by the user. If there are event listeners in place then create
an event object and notify the listeners. */
static void
python_context_changed (user_selected_what selection)
{
if (!gdb_python_initialized)
return;
gdbpy_enter enter_py (current_inferior ()->arch ());
if (evregpy_no_listeners_p (gdb_py_events.selected_context))
return;
gdbpy_ref<> inf_obj (gdbpy_selected_inferior (nullptr, nullptr));
if (inf_obj == nullptr)
{
gdbpy_print_stack ();
return;
}
gdbpy_ref<> thr_obj (gdbpy_selected_thread (nullptr, nullptr));
if (thr_obj == nullptr)
{
gdbpy_print_stack ();
return;
}
gdbpy_ref<> frame_obj;
if (has_stack_frames ())
frame_obj = gdbpy_ref<> (gdbpy_selected_frame (nullptr, nullptr));
else
frame_obj = gdbpy_ref<>::new_reference (Py_None);
if (frame_obj == nullptr)
{
gdbpy_print_stack ();
return;
}
gdbpy_ref<> event
= create_event_object (&selected_context_event_object_type);
if (event == nullptr
|| evpy_add_attribute (event.get (), "inferior", inf_obj.get ()) < 0
|| evpy_add_attribute (event.get (), "thread", thr_obj.get ()) < 0
|| evpy_add_attribute (event.get (), "frame", frame_obj.get ()) < 0
|| evpy_emit_event (event.get (), gdb_py_events.selected_context) < 0)
gdbpy_print_stack ();
}
static int
gdbpy_initialize_inferior ()
{
@ -1027,6 +1079,8 @@ gdbpy_initialize_inferior ()
gdb::observers::inferior_added.attach (python_new_inferior, "py-inferior");
gdb::observers::inferior_removed.attach (python_inferior_deleted,
"py-inferior");
gdb::observers::user_selected_context_changed.attach (python_context_changed,
"py-inferior");
return 0;
}