summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWill Thompson <will@willthompson.co.uk>2019-03-29 08:30:04 +0000
committerWill Thompson <will@willthompson.co.uk>2019-03-31 21:37:31 +0100
commita1b13fb1c873c75daeb2e15fc323fa3db3217026 (patch)
tree634425f6419e3e7a8f0bd50b573e857c5e5c374b
parentf2f34a4e7d7d87fdf4d1079463f8d15a59cc3501 (diff)
Implement recordingstaging/c
-rw-r--r--c-sources/bustle-record-address-dialog.c120
-rw-r--r--c-sources/bustle-record-address-dialog.h22
-rw-r--r--c-sources/bustle-record-address-dialog.ui108
-rw-r--r--c-sources/bustle-viewer.c7
-rw-r--r--c-sources/bustle-window.c331
-rw-r--r--c-sources/bustle-window.h5
-rw-r--r--c-sources/bustle.gresource.xml1
-rw-r--r--c-sources/meson.build2
8 files changed, 555 insertions, 41 deletions
diff --git a/c-sources/bustle-record-address-dialog.c b/c-sources/bustle-record-address-dialog.c
new file mode 100644
index 0000000..0b77428
--- /dev/null
+++ b/c-sources/bustle-record-address-dialog.c
@@ -0,0 +1,120 @@
+#include "bustle-record-address-dialog.h"
+
+struct _BustleRecordAddressDialog
+{
+ GtkDialog parent_instance;
+
+ GtkEntry *recordAddressEntry;
+ GtkButton *recordAddressRecord;
+};
+
+G_DEFINE_TYPE (BustleRecordAddressDialog, bustle_record_address_dialog, GTK_TYPE_DIALOG)
+
+BustleRecordAddressDialog *
+bustle_record_address_dialog_new (GtkWindow *parent)
+{
+ return g_object_new (BUSTLE_TYPE_RECORD_ADDRESS_DIALOG,
+ "transient-for", parent,
+ NULL);
+}
+
+static void
+bustle_record_address_dialog_finalize (GObject *object)
+{
+ BustleRecordAddressDialog *self = (BustleRecordAddressDialog *)object;
+
+ g_debug ("%s: %p", G_STRFUNC, self);
+
+ G_OBJECT_CLASS (bustle_record_address_dialog_parent_class)->finalize (object);
+}
+
+static void
+bustle_record_address_dialog_class_init (BustleRecordAddressDialogClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = bustle_record_address_dialog_finalize;
+
+ gtk_widget_class_set_template_from_resource (widget_class,
+ "/org/freedesktop/Bustle/bustle-record-address-dialog.ui");
+ gtk_widget_class_bind_template_child (widget_class, BustleRecordAddressDialog, recordAddressEntry);
+ gtk_widget_class_bind_template_child (widget_class, BustleRecordAddressDialog, recordAddressRecord);
+}
+
+static void
+entry_changed_cb (GtkEditable *editable,
+ gpointer user_data)
+{
+ BustleRecordAddressDialog *self = BUSTLE_RECORD_ADDRESS_DIALOG (user_data);
+ GtkEntry *entry = GTK_ENTRY (editable);
+ const gchar *text = gtk_entry_get_text (entry);
+ g_autoptr(GError) local_error = NULL;
+ gboolean ok = text && *text && g_dbus_is_supported_address (text, &local_error);
+
+ gtk_widget_set_sensitive (GTK_WIDGET (self->recordAddressRecord), ok);
+ if (ok)
+ {
+ gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, NULL);
+ }
+ else if (text && *text)
+ {
+ gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, "dialog-warning");
+ gtk_entry_set_icon_tooltip_text (entry, GTK_ENTRY_ICON_SECONDARY, local_error->message);
+ }
+}
+
+static void
+bustle_record_address_dialog_init (BustleRecordAddressDialog *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ g_signal_connect_object (self->recordAddressEntry, "changed",
+ G_CALLBACK (entry_changed_cb), self, 0);
+ entry_changed_cb (GTK_EDITABLE (self->recordAddressEntry), self);
+}
+
+static void
+response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ BustleRecordAddressDialog *self = BUSTLE_RECORD_ADDRESS_DIALOG (dialog);
+ g_autoptr(GTask) task = G_TASK (user_data);
+
+ if (response_id == GTK_RESPONSE_ACCEPT)
+ g_task_return_pointer (task,
+ g_strdup (gtk_entry_get_text (self->recordAddressEntry)),
+ g_free);
+ else
+ g_cancellable_cancel (g_task_get_cancellable (task));
+
+ g_signal_handlers_disconnect_by_data (dialog, task);
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+
+void
+bustle_record_address_dialog_run_async (BustleRecordAddressDialog *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = g_task_new (self, cancellable, callback, user_data);
+
+ g_signal_connect (self, "response",
+ G_CALLBACK (response_cb), g_steal_pointer (&task));
+ gtk_widget_show_all (GTK_WIDGET (self));
+}
+
+gchar *
+bustle_record_address_dialog_run_finish (BustleRecordAddressDialog *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (BUSTLE_IS_RECORD_ADDRESS_DIALOG (self), NULL);
+ g_return_val_if_fail (g_task_is_valid (result, self), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
diff --git a/c-sources/bustle-record-address-dialog.h b/c-sources/bustle-record-address-dialog.h
new file mode 100644
index 0000000..9c02382
--- /dev/null
+++ b/c-sources/bustle-record-address-dialog.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define BUSTLE_TYPE_RECORD_ADDRESS_DIALOG (bustle_record_address_dialog_get_type())
+
+G_DECLARE_FINAL_TYPE (BustleRecordAddressDialog, bustle_record_address_dialog, BUSTLE, RECORD_ADDRESS_DIALOG, GtkDialog)
+
+BustleRecordAddressDialog *bustle_record_address_dialog_new (GtkWindow *parent);
+void bustle_record_address_dialog_run_async (BustleRecordAddressDialog *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gchar *bustle_record_address_dialog_run_finish (BustleRecordAddressDialog *self,
+ GAsyncResult *result,
+ GError **error);
+
+
+
+G_END_DECLS
diff --git a/c-sources/bustle-record-address-dialog.ui b/c-sources/bustle-record-address-dialog.ui
new file mode 100644
index 0000000..7215e9d
--- /dev/null
+++ b/c-sources/bustle-record-address-dialog.ui
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <template class="BustleRecordAddressDialog" parent="GtkDialog">
+ <property name="title" translatable="yes">Record Address</property>
+ <property name="can_focus">False</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="recordAddressCancel">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="recordAddressRecord">
+ <property name="label" translatable="yes">_Record</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">6</property>
+ <property name="margin_right">6</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Address:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="recordAddressEntry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="activates_default">True</property>
+ <property name="width_chars">60</property>
+ <property name="placeholder_text" translatable="yes" comments="Just translate the &quot;e.g.&quot;; leave the rest untouched.">e.g. unix:abstract=/tmp/dbus-E5RlEB5Tzu,guid= b1c1921b62283b7b612b57305b20cc28</property>
+ <property name="input_hints">GTK_INPUT_HINT_NO_SPELLCHECK | GTK_INPUT_HINT_NO_EMOJI | GTK_INPUT_HINT_NONE</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">recordAddressCancel</action-widget>
+ <action-widget response="-3">recordAddressRecord</action-widget>
+ </action-widgets>
+ </template>
+</interface>
diff --git a/c-sources/bustle-viewer.c b/c-sources/bustle-viewer.c
index 4ae4264..c8c8e2f 100644
--- a/c-sources/bustle-viewer.c
+++ b/c-sources/bustle-viewer.c
@@ -11,7 +11,8 @@ open_cb (GApplication *application,
{
for (int i = 0; i < n_files; i++)
{
- bustle_window_new (GTK_APPLICATION (application), files[i]);
+ BustleWindow *window = bustle_window_new (GTK_APPLICATION (application));
+ bustle_window_load_file (window, files[i]);
}
}
@@ -19,9 +20,7 @@ static void
activate_cb (GApplication *application,
gpointer user_data)
{
- g_autoptr(GFile) file = g_file_new_for_path ("/home/wjt/src/bustle/oh-no.pcap");
-
- bustle_window_new (GTK_APPLICATION (application), file);
+ bustle_window_new (GTK_APPLICATION (application));
}
gint
diff --git a/c-sources/bustle-window.c b/c-sources/bustle-window.c
index 36d53b0..71eae0d 100644
--- a/c-sources/bustle-window.c
+++ b/c-sources/bustle-window.c
@@ -6,20 +6,41 @@
#include "bustle-model.h"
#include "bustle-name-model.h"
#include "bustle-pcap-reader.h"
+#include "bustle-record-address-dialog.h"
+#include "pcap-monitor.h"
struct _BustleWindow
{
GtkApplicationWindow parent_instance;
GFile *file;
+ BustlePcapMonitor *monitor;
+
+ BustleModel *model;
+ guint messages_logged;
+ /* Timestamp of the first message in @model, or 0 if it is empty. */
+ gint64 first_ts;
GtkStack *diagramOrNot;
GtkScrolledWindow *diagramScrolledWindow;
+ /* Error stuff */
GtkInfoBar *errorBar;
GtkLabel *errorBarTitle;
GtkLabel *errorBarDetails;
+ /* Menu stuff */
+ GtkMenuButton *headerRecord;
+ GtkMenuButton *headerStop;
+ /* TODO: move to actions */
+ GtkMenuItem *recordSession;
+ GtkMenuItem *recordSystem;
+ GtkMenuItem *recordAddress;
+
+ GtkSpinner *headerSpinner;
+ GtkLabel *headerTitle;
+ GtkLabel *headerSubtitle;
+
/* Details stuff */
/* TODO: move to separate widget */
GtkGrid *detailsGrid;
@@ -38,11 +59,15 @@ struct _BustleWindow
G_DEFINE_TYPE (BustleWindow, bustle_window, GTK_TYPE_APPLICATION_WINDOW)
+static void bustle_window_show_model (BustleWindow *self);
static void bustle_window_show_error (BustleWindow *self,
const gchar *title,
const GError *error);
-static gboolean bustle_window_load_file (BustleWindow *self,
- GError **error);
+
+static void record_cb (GtkMenuItem *item,
+ gpointer user_data);
+static void stop_cb (GtkButton *item,
+ gpointer user_data);
typedef enum {
PROP_FILE = 1,
@@ -52,15 +77,12 @@ typedef enum {
static GParamSpec *properties [N_PROPS];
BustleWindow *
-bustle_window_new (GtkApplication *application,
- GFile *file)
+bustle_window_new (GtkApplication *application)
{
g_return_val_if_fail (GTK_IS_APPLICATION (application), NULL);
- g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL);
return g_object_new (BUSTLE_TYPE_WINDOW,
"application", application,
- "file", file,
NULL);
}
@@ -76,24 +98,22 @@ static void
bustle_window_constructed (GObject *object)
{
BustleWindow *self = (BustleWindow *)object;
- g_autoptr(GError) error = NULL;
G_OBJECT_CLASS (bustle_window_parent_class)->constructed (object);
- gtk_widget_show (GTK_WIDGET (self));
+ self->model = bustle_model_new ();
+ bustle_window_show_model (self);
g_signal_connect_object (self->errorBar, "response",
G_CALLBACK (error_bar_response_cb), self,
0);
- g_assert (self->file != NULL);
- if (!bustle_window_load_file (self, &error))
- {
- g_autofree gchar *title = g_strdup_printf (_("Could not read ‘%s’."),
- g_file_peek_path (self->file));
+ g_signal_connect (self->recordSession, "activate", G_CALLBACK (record_cb), self);
+ g_signal_connect (self->recordSystem, "activate", G_CALLBACK (record_cb), self);
+ g_signal_connect (self->recordAddress, "activate", G_CALLBACK (record_cb), self);
+ g_signal_connect (self->headerStop, "clicked", G_CALLBACK (stop_cb), self);
- bustle_window_show_error (self, title, error);
- }
+ gtk_widget_show (GTK_WIDGET (self));
}
static void
@@ -102,6 +122,8 @@ bustle_window_finalize (GObject *object)
BustleWindow *self = (BustleWindow *)object;
g_clear_object (&self->file);
+ g_clear_object (&self->monitor);
+ g_clear_object (&self->model);
G_OBJECT_CLASS (bustle_window_parent_class)->finalize (object);
}
@@ -162,8 +184,7 @@ bustle_window_class_init (BustleWindowClass *klass)
"File",
"File",
G_TYPE_FILE,
- (G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE |
+ (G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_FILE,
properties [PROP_FILE]);
@@ -177,6 +198,16 @@ bustle_window_class_init (BustleWindowClass *klass)
gtk_widget_class_bind_template_child (widget_class, BustleWindow, errorBarTitle);
gtk_widget_class_bind_template_child (widget_class, BustleWindow, errorBarDetails);
+ gtk_widget_class_bind_template_child (widget_class, BustleWindow, headerRecord);
+ gtk_widget_class_bind_template_child (widget_class, BustleWindow, headerStop);
+ gtk_widget_class_bind_template_child (widget_class, BustleWindow, recordSession);
+ gtk_widget_class_bind_template_child (widget_class, BustleWindow, recordSystem);
+ gtk_widget_class_bind_template_child (widget_class, BustleWindow, recordAddress);
+
+ gtk_widget_class_bind_template_child (widget_class, BustleWindow, headerSpinner);
+ gtk_widget_class_bind_template_child (widget_class, BustleWindow, headerTitle);
+ gtk_widget_class_bind_template_child (widget_class, BustleWindow, headerSubtitle);
+
gtk_widget_class_bind_template_child (widget_class, BustleWindow, detailsGrid);
gtk_widget_class_bind_template_child (widget_class, BustleWindow, detailsType);
gtk_widget_class_bind_template_child (widget_class, BustleWindow, detailsSender);
@@ -204,14 +235,14 @@ timestamp_data_func (GtkTreeViewColumn *tree_column,
GtkTreeIter *iter,
gpointer data)
{
- gint64 *first_ts = data;
+ BustleWindow *self = BUSTLE_WINDOW (data);
gint64 timestamp_usec;
g_autofree gchar *text = NULL;
gtk_tree_model_get (tree_model, iter,
BUSTLE_MODEL_COLUMN_TIMESTAMP_USEC, &timestamp_usec,
-1);
- text = g_strdup_printf ("%.3fs", (double) (timestamp_usec - *first_ts) / G_USEC_PER_SEC);
+ text = g_strdup_printf ("+%.3fs", (double) (timestamp_usec - self->first_ts) / G_USEC_PER_SEC);
g_object_set (cell, "text", text, NULL);
}
@@ -494,16 +525,12 @@ selection_changed_cb (GtkTreeSelection *selection,
}
static void
-bustle_window_show_model (BustleWindow *self,
- GtkTreeModel *model)
+bustle_window_show_model (BustleWindow *self)
{
- g_autoptr(GtkTreePath) path = gtk_tree_path_new_from_string ("0");
- GtkTreeIter iter;
- gint64 *first_ts = g_new (gint64, 1);
-
- gtk_tree_model_get_iter (model, &iter, path);
- gtk_tree_model_get (model, &iter, BUSTLE_MODEL_COLUMN_TIMESTAMP_USEC, first_ts, -1);
+ /*
+ */
+ GtkTreeModel *model = bustle_model_get_tree_model (self->model);
GtkWidget *tree_view = gtk_tree_view_new_with_model (model);
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
@@ -519,7 +546,7 @@ bustle_window_show_model (BustleWindow *self,
column = gtk_tree_view_column_new_with_attributes ("Timestamp", renderer, NULL);
gtk_tree_view_column_set_cell_data_func (column, renderer,
timestamp_data_func,
- g_steal_pointer (&first_ts), g_free);
+ self, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
renderer = gtk_cell_renderer_text_new ();
@@ -559,13 +586,26 @@ bustle_window_show_model (BustleWindow *self,
selection_changed_cb (selection, self);
gtk_container_add (GTK_CONTAINER (self->diagramScrolledWindow), tree_view);
- gtk_stack_set_visible_child_name (self->diagramOrNot, "CanvasPage");
gtk_widget_show (tree_view);
}
+static void
+bustle_window_add_message (BustleWindow *self,
+ gint64 ts,
+ GDBusMessage *message)
+{
+ if (self->first_ts == 0)
+ {
+ self->first_ts = ts;
+ gtk_stack_set_visible_child_name (self->diagramOrNot, "CanvasPage");
+ }
+
+ bustle_model_add_message (self->model, ts, message);
+}
+
static gboolean
-bustle_window_load_file (BustleWindow *self,
- GError **error)
+bustle_window_do_load_file (BustleWindow *self,
+ GError **error)
{
g_assert (self->file != NULL);
@@ -574,8 +614,6 @@ bustle_window_load_file (BustleWindow *self,
if (reader == NULL)
return FALSE;
- g_autoptr(BustleModel) model = bustle_model_new ();
-
gint64 ts;
g_autoptr(GDBusMessage) message = NULL;
@@ -584,11 +622,10 @@ bustle_window_load_file (BustleWindow *self,
if (message == NULL)
{
/* EOF */
- bustle_window_show_model (self, bustle_model_get_tree_model (model));
return TRUE;
}
- bustle_model_add_message (model, ts, message);
+ bustle_window_add_message (self, ts, message);
}
return FALSE;
@@ -615,3 +652,227 @@ bustle_window_show_error (BustleWindow *self,
/* TODO: use :revealed, modulo https://gitlab.gnome.org/GNOME/gtk/issues/1165 */
gtk_widget_show (GTK_WIDGET (self->errorBar));
}
+
+void
+bustle_window_load_file (BustleWindow *self,
+ GFile *file)
+{
+ g_return_if_fail (BUSTLE_IS_WINDOW (self));
+ g_return_if_fail (G_IS_FILE (file));
+
+ g_return_if_fail (self->file == NULL);
+ self->file = g_object_ref (file);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FILE]);
+
+ g_autoptr(GError) local_error = NULL;
+
+ if (!bustle_window_do_load_file (self, &local_error))
+ {
+ g_autofree gchar *title = g_strdup_printf (_("Could not read ‘%s’."),
+ g_file_peek_path (self->file));
+ bustle_window_show_error (self, title, local_error);
+ }
+}
+
+static void
+message_logged_cb (BustlePcapMonitor *monitor,
+ long timestamp_sec,
+ long timestamp_usec,
+ const guint8 *blob,
+ gsize len,
+ gpointer user_data)
+{
+ BustleWindow *self = BUSTLE_WINDOW (user_data);
+ gint64 timestamp = G_USEC_PER_SEC * timestamp_sec + timestamp_usec;
+ g_autoptr(GError) local_error = NULL;
+ g_autoptr(GDBusMessage) message = NULL;
+
+ message = g_dbus_message_new_from_blob ((guchar *) blob, len,
+ G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING,
+ &local_error);
+ if (message == NULL)
+ {
+ bustle_window_show_error (self,
+ _("Could not decode recorded message."),
+ local_error);
+ bustle_pcap_monitor_stop (self->monitor);
+ }
+ else
+ {
+ bustle_window_add_message (self, timestamp, message);
+ self->messages_logged++;
+
+ g_autofree gchar *subtitle = g_strdup_printf (_("%u messages"),
+ self->messages_logged);
+ gtk_label_set_text (self->headerSubtitle, subtitle);
+ }
+}
+
+static void
+stopped_cb (BustlePcapMonitor *monitor,
+ guint domain,
+ gint code,
+ const gchar *message,
+ gpointer user_data)
+{
+ BustleWindow *self = BUSTLE_WINDOW (user_data);
+ g_assert (domain != 0);
+ g_assert (domain <= G_MAXUINT32);
+ g_autoptr(GError) local_error = g_error_new_literal ((GQuark) domain, code, message);
+
+ gtk_spinner_stop (self->headerSpinner);
+ gtk_widget_show (GTK_WIDGET (self->headerRecord));
+ gtk_widget_hide (GTK_WIDGET (self->headerStop));
+ if (self->first_ts == 0)
+ {
+ gtk_stack_set_visible_child_name (self->diagramOrNot, "InstructionsPage");
+ g_clear_object (&self->file);
+ }
+
+ if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ bustle_window_show_error (self,
+ _("Recording failed."),
+ local_error);
+
+ g_signal_handlers_disconnect_by_data (self->monitor, self);
+ g_clear_object (&self->monitor);
+}
+
+static void
+bustle_window_start_recording (BustleWindow *self,
+ GBusType bus_type,
+ const gchar *address)
+{
+ g_assert (BUSTLE_IS_WINDOW (self));
+ g_assert ((bus_type == G_BUS_TYPE_NONE) == (address != NULL));
+ g_assert (self->file == NULL);
+ g_assert (self->monitor == NULL);
+
+ g_autoptr(GDateTime) now = g_date_time_new_now_local ();
+ g_autofree gchar *basename = g_date_time_format (now,
+ "%F %H-%M-%S.pcap");
+ g_autofree gchar *target_path = g_build_filename (g_get_user_cache_dir (),
+ "bustle",
+ basename,
+ NULL);
+ g_autoptr(GFile) target_file = g_file_new_for_path (target_path);
+ g_autoptr(GFile) target_dir = g_file_get_parent (target_file);
+ g_autoptr(GError) local_error = NULL;
+
+ if (!g_file_make_directory_with_parents (target_dir, NULL, &local_error) &&
+ !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+ {
+ bustle_window_show_error (self,
+ _("Could not start recording."),
+ local_error);
+ return;
+ }
+
+ g_clear_error (&local_error);
+ self->monitor = bustle_pcap_monitor_new (bus_type, address, target_path,
+ &local_error);
+ if (self->monitor == NULL)
+ {
+ bustle_window_show_error (self,
+ _("Could not start recording."),
+ local_error);
+ }
+ else
+ {
+ g_autofree gchar *title_to_free = NULL;
+ const gchar *title = NULL;
+ switch (bus_type)
+ {
+ case G_BUS_TYPE_SESSION:
+ title = _("Recording session bus…");
+ break;
+ case G_BUS_TYPE_SYSTEM:
+ title = _("Recording system bus…");
+ break;
+ case G_BUS_TYPE_NONE:
+ title_to_free = g_strdup_printf (_("Recording %s…"), address);
+ title = title_to_free;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ g_set_object (&self->file, target_file);
+
+ gtk_stack_set_visible_child_name (self->diagramOrNot, "PleaseHoldPage");
+
+ gtk_spinner_start (self->headerSpinner);
+ gtk_label_set_text (self->headerTitle, title);
+
+ gtk_widget_hide (GTK_WIDGET (self->headerRecord));
+ gtk_widget_show (GTK_WIDGET (self->headerStop));
+
+ g_signal_connect_object (self->monitor, "message-logged",
+ G_CALLBACK (message_logged_cb), self, 0);
+ g_signal_connect_object (self->monitor, "stopped",
+ G_CALLBACK (stopped_cb), self, 0);
+ }
+}
+
+static void
+bustle_window_start_recording_somewhere (BustleWindow *self,
+ GBusType bus_type,
+ const gchar *address)
+{
+ BustleWindow *target;
+
+ if (self->first_ts != 0)
+ target = bustle_window_new (gtk_window_get_application (GTK_WINDOW (self)));
+ else
+ target = self;
+
+ bustle_window_start_recording (target, bus_type, address);
+}
+
+static void
+record_address_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr(BustleWindow) self = BUSTLE_WINDOW (user_data);
+ g_autoptr(GError) local_error = NULL;
+ g_autofree gchar *address = NULL;
+
+ address = bustle_record_address_dialog_run_finish (BUSTLE_RECORD_ADDRESS_DIALOG (source),
+ result,
+ &local_error);
+ if (address != NULL)
+ bustle_window_start_recording_somewhere (self, G_BUS_TYPE_NONE, address);
+ else if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("%s", local_error->message);
+}
+
+static void
+record_cb (GtkMenuItem *item,
+ gpointer user_data)
+{
+ BustleWindow *self = BUSTLE_WINDOW (user_data);
+
+ if (self->recordSession == item)
+ bustle_window_start_recording_somewhere (self, G_BUS_TYPE_SESSION, NULL);
+ else if (self->recordSystem == item)
+ bustle_window_start_recording_somewhere (self, G_BUS_TYPE_SYSTEM, NULL);
+ else if (self->recordAddress == item)
+ {
+ BustleRecordAddressDialog *d = bustle_record_address_dialog_new (GTK_WINDOW (self));
+ bustle_record_address_dialog_run_async (d, NULL, record_address_cb, g_object_ref (self));
+ }
+ else
+ g_assert_not_reached ();
+}
+
+static void
+stop_cb (GtkButton *item,
+ gpointer user_data)
+{
+ BustleWindow *self = BUSTLE_WINDOW (user_data);
+
+ g_assert (self->monitor != NULL);
+
+ bustle_pcap_monitor_stop (self->monitor);
+}
diff --git a/c-sources/bustle-window.h b/c-sources/bustle-window.h
index 39f5960..dd2cec3 100644
--- a/c-sources/bustle-window.h
+++ b/c-sources/bustle-window.h
@@ -8,7 +8,8 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (BustleWindow, bustle_window, BUSTLE, WINDOW, GtkApplicationWindow)
-BustleWindow *bustle_window_new (GtkApplication *application,
- GFile *file);
+BustleWindow *bustle_window_new (GtkApplication *application);
+void bustle_window_load_file (BustleWindow *self,
+ GFile *file);
G_END_DECLS
diff --git a/c-sources/bustle.gresource.xml b/c-sources/bustle.gresource.xml
index 7287aa9..e729669 100644
--- a/c-sources/bustle.gresource.xml
+++ b/c-sources/bustle.gresource.xml
@@ -2,6 +2,7 @@
<gresources>
<gresource prefix="/org/freedesktop/Bustle">
<file preprocess="xml-stripblanks">bustle-window.ui</file>
+ <file preprocess="xml-stripblanks">bustle-record-address-dialog.ui</file>
</gresource>
</gresources>
diff --git a/c-sources/meson.build b/c-sources/meson.build
index 9a6c889..2e0da04 100644
--- a/c-sources/meson.build
+++ b/c-sources/meson.build
@@ -49,6 +49,8 @@ bustle_sources = [
'bustle-viewer.c',
'bustle-pcap-reader.c',
'bustle-pcap-reader.h',
+ 'bustle-record-address-dialog.c',
+ 'bustle-record-address-dialog.h',
bustle_resources,
]