[gnome-builder/wip/chergert/sysprof] sysprof: stub out sysprof perspective
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/chergert/sysprof] sysprof: stub out sysprof perspective
- Date: Mon, 22 Aug 2016 23:03:03 +0000 (UTC)
commit a4277e8d6e33b9bcf10c1d37e0fbfa54e15f2256
Author: Christian Hergert <chergert redhat com>
Date: Mon Aug 22 16:02:42 2016 -0700
sysprof: stub out sysprof perspective
configure.ac | 2 +
data/theme/Adwaita-shared.css | 6 +-
data/theme/shared.css | 5 +
libide/Makefile.am | 3 +
libide/resources/libide.gresource.xml | 1 +
libide/runner/OVERVIEW.md | 11 +
libide/runner/ide-run-button.c | 250 ++++++++++++
libide/runner/ide-run-button.h | 34 ++
libide/runner/ide-run-button.ui | 70 ++++
libide/runner/ide-run-manager-private.h | 42 ++
libide/runner/ide-run-manager.c | 439 ++++++++++++++++++++--
libide/runner/ide-run-manager.h | 48 ++-
libide/runner/ide-runner.c | 23 ++
libide/workbench/ide-perspective-menu-button.ui | 2 +-
libide/workbench/ide-workbench-header-bar.ui | 9 +
libide/workers/ide-subprocess-launcher.c | 5 +
plugins/Makefile.am | 1 +
plugins/autotools/ide-makecache.c | 1 +
plugins/run-tools/gbp-run-workbench-addin.c | 129 +------
plugins/sysprof/Makefile.am | 37 ++
plugins/sysprof/configure.ac | 21 +
plugins/sysprof/gbp-sysprof-perspective.c | 184 +++++++++
plugins/sysprof/gbp-sysprof-perspective.h | 36 ++
plugins/sysprof/gbp-sysprof-perspective.ui | 15 +
plugins/sysprof/gbp-sysprof-plugin.c | 30 ++
plugins/sysprof/gbp-sysprof-workbench-addin.c | 460 +++++++++++++++++++++++
plugins/sysprof/gbp-sysprof-workbench-addin.h | 33 ++
plugins/sysprof/gtk/menus.ui | 11 +
plugins/sysprof/sysprof.gresource.xml | 7 +
plugins/sysprof/sysprof.plugin | 7 +
30 files changed, 1753 insertions(+), 169 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index c896021..8f09661 100644
--- a/configure.ac
+++ b/configure.ac
@@ -308,6 +308,7 @@ m4_include([plugins/run-tools/configure.ac])
m4_include([plugins/support/configure.ac])
m4_include([plugins/symbol-tree/configure.ac])
m4_include([plugins/sysmon/configure.ac])
+m4_include([plugins/sysprof/configure.ac])
m4_include([plugins/todo/configure.ac])
m4_include([plugins/terminal/configure.ac])
m4_include([plugins/vala-pack/configure.ac])
@@ -595,6 +596,7 @@ echo " Python Language Pack ................. : ${enable_python_pack_plugin}"
echo " Run Tools ............................ : ${enable_run_tools_plugin}"
echo " Support .............................. : ${enable_support_plugin}"
echo " System Monitor ....................... : ${enable_sysmon_plugin}"
+echo " Sysprof System Profiler .............. : ${enable_sysprof_plugin}"
echo " Symbol Tree .......................... : ${enable_symbol_tree_plugin}"
echo " Todo ................................. : ${enable_todo_plugin}"
echo " Terminal ............................. : ${enable_terminal_plugin}"
diff --git a/data/theme/Adwaita-shared.css b/data/theme/Adwaita-shared.css
index cf05cb0..37289a3 100644
--- a/data/theme/Adwaita-shared.css
+++ b/data/theme/Adwaita-shared.css
@@ -132,15 +132,15 @@ treeview.image { color: alpha(currentColor, 0.8); }
treeview.image:selected { color: alpha(@theme_selected_fg_color, 0.9); }
-popover.perspectives-selector list {
+popover.popover-selector list {
padding: 12px;
}
-popover.perspectives-selector list row {
+popover.popover-selector list row {
padding: 8px;
}
-popover.perspectives-selector list row image {
+popover.popover-selector list row image {
min-height: 16px;
min-width: 16px;
margin-right: 12px;
diff --git a/data/theme/shared.css b/data/theme/shared.css
index 705dcb7..92d723e 100644
--- a/data/theme/shared.css
+++ b/data/theme/shared.css
@@ -231,3 +231,8 @@ textview border.left {
treeview.dim-label {
color: alpha(currentColor, 0.5);
}
+
+
+button.run-arrow-button {
+ min-width: 12px;
+}
diff --git a/libide/Makefile.am b/libide/Makefile.am
index 402372a..94e709c 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -89,6 +89,7 @@ libide_1_0_la_public_headers = \
projects/ide-project-miner.h \
projects/ide-project.h \
projects/ide-recent-projects.h \
+ runner/ide-run-button.h \
runner/ide-run-manager.h \
runner/ide-runner.h \
runner/ide-runner-addin.h \
@@ -240,6 +241,7 @@ libide_1_0_la_public_sources = \
projects/ide-project-miner.c \
projects/ide-project.c \
projects/ide-recent-projects.c \
+ runner/ide-run-button.c \
runner/ide-run-manager.c \
runner/ide-runner.c \
runner/ide-runner-addin.c \
@@ -377,6 +379,7 @@ libide_1_0_la_SOURCES = \
preferences/ide-preferences-page-private.h \
preferences/ide-preferences-perspective.c \
preferences/ide-preferences-perspective.h \
+ runner/ide-run-manager-private.h \
search/ide-search-reducer.c \
snippets/ide-source-snippet-completion-item.c \
snippets/ide-source-snippet-completion-item.h \
diff --git a/libide/resources/libide.gresource.xml b/libide/resources/libide.gresource.xml
index 63254ca..ecf67b7 100644
--- a/libide/resources/libide.gresource.xml
+++ b/libide/resources/libide.gresource.xml
@@ -75,6 +75,7 @@
<file compressed="true"
alias="ide-preferences-perspective.ui">../preferences/ide-preferences-perspective.ui</file>
<file compressed="true"
alias="ide-preferences-spin-button.ui">../preferences/ide-preferences-spin-button.ui</file>
<file compressed="true" alias="ide-preferences-switch.ui">../preferences/ide-preferences-switch.ui</file>
+ <file compressed="true" alias="ide-run-button.ui">../runner/ide-run-button.ui</file>
<file compressed="true" alias="ide-shortcuts-window.ui">../keybindings/ide-shortcuts-window.ui</file>
<file compressed="true"
alias="ide-workbench-header-bar.ui">../workbench/ide-workbench-header-bar.ui</file>
<file compressed="true" alias="ide-workbench.ui">../workbench/ide-workbench.ui</file>
diff --git a/libide/runner/OVERVIEW.md b/libide/runner/OVERVIEW.md
index cf5801a..94c4e33 100644
--- a/libide/runner/OVERVIEW.md
+++ b/libide/runner/OVERVIEW.md
@@ -87,3 +87,14 @@ So it might need to do something like:
runner.prepend_argv('sysprof-spawner')
+
+## RunHandlers
+
+Because we want one runner at a time, and different plugins might implement
+different runners (debugger, profiler, basic run support), we register
+a run handler.
+
+When ide_run_manager_run_async() is called, the run handler will be called
+and it can adjust things as necessary.
+
+
diff --git a/libide/runner/ide-run-button.c b/libide/runner/ide-run-button.c
new file mode 100644
index 0000000..cccc5bb
--- /dev/null
+++ b/libide/runner/ide-run-button.c
@@ -0,0 +1,250 @@
+/* ide-run-button.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-run-button"
+
+#include <glib/gi18n.h>
+
+#include "ide-context.h"
+#include "ide-debug.h"
+
+#include "runner/ide-run-button.h"
+#include "runner/ide-run-manager.h"
+#include "runner/ide-run-manager-private.h"
+#include "util/ide-gtk.h"
+
+struct _IdeRunButton
+{
+ GtkBox parent_instance;
+
+ GtkSizeGroup *accel_size_group;
+ GtkButton *button;
+ GtkImage *button_image;
+ GtkListBox *list_box;
+ GtkMenuButton *menu_button;
+ GtkPopover *popover;
+ GtkButton *stop_button;
+};
+
+G_DEFINE_TYPE (IdeRunButton, ide_run_button, GTK_TYPE_BOX)
+
+static GtkWidget *
+create_row (const IdeRunHandlerInfo *info,
+ IdeRunButton *self)
+{
+ GtkListBoxRow *row;
+ GtkLabel *label;
+ GtkImage *image;
+ GtkBox *box;
+
+ g_assert (info != NULL);
+ g_assert (IDE_IS_RUN_BUTTON (self));
+
+ row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
+ "can-focus", FALSE,
+ "selectable", FALSE,
+ "visible", TRUE,
+ NULL);
+
+ g_object_set_data_full (G_OBJECT (row), "IDE_RUN_HANDLER_ID", g_strdup (info->id), g_free);
+
+ box = g_object_new (GTK_TYPE_BOX,
+ "visible", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (row), GTK_WIDGET (box));
+
+ image = g_object_new (GTK_TYPE_IMAGE,
+ "hexpand", FALSE,
+ "icon-name", info->icon_name,
+ "visible", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (image));
+
+ label = g_object_new (GTK_TYPE_LABEL,
+ "label", info->title,
+ "hexpand", TRUE,
+ "xalign", 0.0f,
+ "visible", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (label));
+
+ if (info->accel != NULL)
+ {
+ g_autofree gchar *xaccel = NULL;
+ guint accel_key = 0;
+ GdkModifierType accel_mod = 0;
+
+ gtk_accelerator_parse (info->accel, &accel_key, &accel_mod);
+ xaccel = gtk_accelerator_get_label (accel_key, accel_mod);
+ label = g_object_new (GTK_TYPE_LABEL,
+ "label", xaccel,
+ "margin-start", 20,
+ "visible", TRUE,
+ "xalign", 0.0f,
+ NULL);
+ ide_widget_add_style_class (GTK_WIDGET (label), "dim-label");
+ gtk_container_add_with_properties (GTK_CONTAINER (box), GTK_WIDGET (label),
+ "pack-type", GTK_PACK_END,
+ NULL);
+ gtk_size_group_add_widget (self->accel_size_group, GTK_WIDGET (label));
+ }
+
+ return GTK_WIDGET (row);
+}
+
+static void
+ide_run_button_clear (IdeRunButton *self)
+{
+ g_assert (IDE_IS_RUN_BUTTON (self));
+
+ gtk_container_foreach (GTK_CONTAINER (self->list_box), (GtkCallback)gtk_widget_destroy, NULL);
+}
+
+static void
+ide_run_button_handler_set (IdeRunButton *self,
+ GParamSpec *pspec,
+ IdeRunManager *run_manager)
+{
+ const GList *list;
+ const GList *iter;
+ const gchar *handler;
+
+ g_assert (IDE_IS_RUN_BUTTON (self));
+ g_assert (IDE_IS_RUN_MANAGER (run_manager));
+
+ handler = ide_run_manager_get_handler (run_manager);
+ list = _ide_run_manager_get_handlers (run_manager);
+
+ for (iter = list; iter; iter = iter->next)
+ {
+ const IdeRunHandlerInfo *info = iter->data;
+
+ if (g_strcmp0 (info->id, handler) == 0)
+ {
+ g_object_set (self->button_image,
+ "icon-name", info->icon_name,
+ NULL);
+ break;
+ }
+ }
+}
+
+static void
+ide_run_button_load (IdeRunButton *self,
+ IdeContext *context)
+{
+ IdeRunManager *run_manager;
+ const GList *list;
+ const GList *iter;
+
+ g_assert (IDE_IS_RUN_BUTTON (self));
+ g_assert (IDE_IS_CONTEXT (context));
+
+ run_manager = ide_context_get_run_manager (context);
+ list = _ide_run_manager_get_handlers (run_manager);
+
+ for (iter = list; iter; iter = iter->next)
+ {
+ const IdeRunHandlerInfo *info = iter->data;
+ GtkWidget *row;
+
+ row = create_row (info, self);
+
+ gtk_container_add (GTK_CONTAINER (self->list_box), row);
+ }
+
+ g_object_bind_property (run_manager, "busy", self->button, "visible",
+ G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
+ g_object_bind_property (run_manager, "busy", self->stop_button, "visible",
+ G_BINDING_SYNC_CREATE);
+
+ g_signal_connect_object (run_manager,
+ "notify::handler",
+ G_CALLBACK (ide_run_button_handler_set),
+ self,
+ G_CONNECT_SWAPPED);
+}
+
+static void
+ide_run_button_row_activated (IdeRunButton *self,
+ GtkListBoxRow *row,
+ GtkListBox *list_box)
+{
+ IdeContext *context;
+ const gchar *id;
+
+ g_assert (IDE_IS_RUN_BUTTON (self));
+ g_assert (GTK_IS_LIST_BOX_ROW (row));
+ g_assert (GTK_IS_LIST_BOX (list_box));
+
+ context = ide_widget_get_context (GTK_WIDGET (self));
+ id = g_object_get_data (G_OBJECT (row), "IDE_RUN_HANDLER_ID");
+
+ if (id != NULL && context != NULL)
+ {
+ IdeRunManager *run_manager;
+
+ run_manager = ide_context_get_run_manager (context);
+ ide_run_manager_set_handler (run_manager, id);
+ gtk_widget_hide (GTK_WIDGET (self->popover));
+ }
+}
+
+static void
+ide_run_button_context_set (GtkWidget *widget,
+ IdeContext *context)
+{
+ IdeRunButton *self = (IdeRunButton *)widget;
+
+ g_assert (IDE_IS_RUN_BUTTON (self));
+ g_assert (!context || IDE_IS_CONTEXT (context));
+
+ ide_run_button_clear (self);
+
+ if (context != NULL)
+ ide_run_button_load (self, context);
+}
+
+static void
+ide_run_button_class_init (IdeRunButtonClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/builder/ui/ide-run-button.ui");
+ gtk_widget_class_bind_template_child (widget_class, IdeRunButton, accel_size_group);
+ gtk_widget_class_bind_template_child (widget_class, IdeRunButton, button);
+ gtk_widget_class_bind_template_child (widget_class, IdeRunButton, button_image);
+ gtk_widget_class_bind_template_child (widget_class, IdeRunButton, list_box);
+ gtk_widget_class_bind_template_child (widget_class, IdeRunButton, menu_button);
+ gtk_widget_class_bind_template_child (widget_class, IdeRunButton, popover);
+ gtk_widget_class_bind_template_child (widget_class, IdeRunButton, stop_button);
+}
+
+static void
+ide_run_button_init (IdeRunButton *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ g_signal_connect_object (self->list_box,
+ "row-activated",
+ G_CALLBACK (ide_run_button_row_activated),
+ self,
+ G_CONNECT_SWAPPED);
+
+ ide_widget_set_context_handler (self, ide_run_button_context_set);
+}
diff --git a/libide/runner/ide-run-button.h b/libide/runner/ide-run-button.h
new file mode 100644
index 0000000..94f99cd
--- /dev/null
+++ b/libide/runner/ide-run-button.h
@@ -0,0 +1,34 @@
+/* ide-run-button.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_RUN_BUTTON_H
+#define IDE_RUN_BUTTON_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_RUN_BUTTON (ide_run_button_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeRunButton, ide_run_button, IDE, RUN_BUTTON, GtkBox)
+
+GtkWidget *ide_run_button_new (void);
+
+G_END_DECLS
+
+#endif /* IDE_RUN_BUTTON_H */
diff --git a/libide/runner/ide-run-button.ui b/libide/runner/ide-run-button.ui
new file mode 100644
index 0000000..b387650
--- /dev/null
+++ b/libide/runner/ide-run-button.ui
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <object class="GtkPopover" id="popover">
+ <style>
+ <class name="popover-selector"/>
+ </style>
+ <child>
+ <object class="GtkListBox" id="list_box">
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ <template class="IdeRunButton" parent="GtkBox">
+ <property name="orientation">horizontal</property>
+ <style>
+ <class name="linked"/>
+ </style>
+ <child>
+ <object class="GtkButton" id="button">
+ <property name="action-name">run-tools.run</property>
+ <property name="focus-on-click">false</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage" id="button_image">
+ <property name="icon-name">media-playback-start-symbolic</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="stop_button">
+ <property name="action-name">run-tools.stop</property>
+ <property name="focus-on-click">false</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">media-playback-stop-symbolic</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuButton" id="menu_button">
+ <property name="focus-on-click">false</property>
+ <property name="popover">popover</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="image-button"/>
+ <class name="run-arrow-button"/>
+ </style>
+ <child>
+ <object class="GtkImage" id="menu_button_image">
+ <property name="icon-name">pan-down-symbolic</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkSizeGroup" id="accel_size_group">
+ <property name="mode">horizontal</property>
+ </object>
+</interface>
diff --git a/libide/runner/ide-run-manager-private.h b/libide/runner/ide-run-manager-private.h
new file mode 100644
index 0000000..9acad53
--- /dev/null
+++ b/libide/runner/ide-run-manager-private.h
@@ -0,0 +1,42 @@
+/* ide-run-manager-private.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_RUN_MANAGER_PRIVATE_H
+#define IDE_RUN_MANAGER_PRIVATE_H
+
+#include "ide-run-manager.h"
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+ gchar *id;
+ gchar *title;
+ gchar *icon_name;
+ gchar *accel;
+ gint priority;
+ IdeRunHandler handler;
+ gpointer handler_data;
+ GDestroyNotify handler_data_destroy;
+} IdeRunHandlerInfo;
+
+const GList *_ide_run_manager_get_handlers (IdeRunManager *self);
+
+G_END_DECLS
+
+#endif /* IDE_RUN_MANAGER_PRIVATE_H */
diff --git a/libide/runner/ide-run-manager.c b/libide/runner/ide-run-manager.c
index b86d93a..2fe35c6 100644
--- a/libide/runner/ide-run-manager.c
+++ b/libide/runner/ide-run-manager.c
@@ -24,21 +24,27 @@
#include "ide-debug.h"
#include "buildsystem/ide-build-manager.h"
+#include "buildsystem/ide-build-system.h"
#include "buildsystem/ide-build-target.h"
#include "buildsystem/ide-configuration.h"
#include "buildsystem/ide-configuration-manager.h"
#include "runner/ide-run-manager.h"
+#include "runner/ide-run-manager-private.h"
#include "runner/ide-runner.h"
#include "runtimes/ide-runtime.h"
struct _IdeRunManager
{
- IdeObject parent_instance;
+ IdeObject parent_instance;
- GCancellable *cancellable;
- GSimpleActionGroup *actions;
+ GCancellable *cancellable;
+ GSimpleActionGroup *actions;
+ IdeBuildTarget *build_target;
- guint busy : 1;
+ const IdeRunHandlerInfo *handler;
+ GList *handlers;
+
+ guint busy : 1;
};
G_DEFINE_TYPE (IdeRunManager, ide_run_manager, IDE_TYPE_OBJECT)
@@ -46,10 +52,33 @@ G_DEFINE_TYPE (IdeRunManager, ide_run_manager, IDE_TYPE_OBJECT)
enum {
PROP_0,
PROP_BUSY,
+ PROP_HANDLER,
N_PROPS
};
+enum {
+ STOPPED,
+ N_SIGNALS
+};
+
static GParamSpec *properties [N_PROPS];
+static guint signals [N_SIGNALS];
+
+static void
+ide_run_handler_info_free (gpointer data)
+{
+ IdeRunHandlerInfo *info = data;
+
+ g_free (info->id);
+ g_free (info->title);
+ g_free (info->icon_name);
+ g_free (info->accel);
+
+ if (info->handler_data_destroy)
+ info->handler_data_destroy (info->handler_data);
+
+ g_slice_free (IdeRunHandlerInfo, info);
+}
static void
ide_run_manager_finalize (GObject *object)
@@ -58,6 +87,10 @@ ide_run_manager_finalize (GObject *object)
g_clear_object (&self->cancellable);
g_clear_object (&self->actions);
+ g_clear_object (&self->build_target);
+
+ g_list_free_full (self->handlers, ide_run_handler_info_free);
+ self->handlers = NULL;
G_OBJECT_CLASS (ide_run_manager_parent_class)->finalize (object);
}
@@ -76,6 +109,10 @@ ide_run_manager_get_property (GObject *object,
g_value_set_boolean (value, ide_run_manager_get_busy (self));
break;
+ case PROP_HANDLER:
+ g_value_set_string (value, ide_run_manager_get_handler (self));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -96,12 +133,44 @@ ide_run_manager_class_init (IdeRunManagerClass *klass)
FALSE,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ properties [PROP_HANDLER] =
+ g_param_spec_string ("handler",
+ "Handler",
+ "Handler",
+ "run",
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ /**
+ * IdeRunManager::stopped:
+ *
+ * This signal is emitted when the run manager has stopped the currently
+ * executing inferior.
+ */
+ signals [STOPPED] =
+ g_signal_new ("stopped",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 0);
}
static void
ide_run_manager_init (IdeRunManager *self)
{
+ ide_run_manager_add_handler (self,
+ "run",
+ _("Run"),
+ "media-playback-start-symbolic",
+ "<Control>F5",
+ NULL,
+ NULL,
+ NULL);
}
gboolean
@@ -139,6 +208,7 @@ ide_run_manager_run_cb (GObject *object,
{
IdeRunner *runner = (IdeRunner *)object;
g_autoptr(GTask) task = user_data;
+ IdeRunManager *self;
GError *error = NULL;
IDE_ENTRY;
@@ -146,6 +216,8 @@ ide_run_manager_run_cb (GObject *object,
g_assert (IDE_IS_RUNNER (runner));
g_assert (G_IS_TASK (task));
+ self = g_task_get_source_object (task);
+
if (!ide_runner_run_finish (runner, result, &error))
{
g_task_return_error (task, error);
@@ -155,6 +227,9 @@ ide_run_manager_run_cb (GObject *object,
g_task_return_boolean (task, TRUE);
failure:
+
+ g_signal_emit (self, signals [STOPPED], 0);
+
IDE_EXIT;
}
@@ -217,6 +292,13 @@ ide_run_manager_install_cb (GObject *object,
g_assert (IDE_IS_RUNNER (runner));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+ /*
+ * If the current handler has a callback specified (our default "run" handler
+ * does not), then we need to allow that handler to prepare the runner.
+ */
+ if (self->handler != NULL && self->handler->handler != NULL)
+ self->handler->handler (self, runner, self->handler->handler_data);
+
ide_runner_run_async (runner,
cancellable,
ide_run_manager_run_cb,
@@ -244,6 +326,66 @@ ide_run_manager_task_completed (IdeRunManager *self,
IDE_EXIT;
}
+static void
+ide_run_manager_do_install_before_run (IdeRunManager *self,
+ GTask *task)
+{
+ IdeBuildManager *build_manager;
+ IdeContext *context;
+
+ g_assert (IDE_IS_RUN_MANAGER (self));
+ g_assert (G_IS_TASK (task));
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+ build_manager = ide_context_get_build_manager (context);
+
+ /*
+ * First we need to make sure the target is up to date and installed
+ * so that all the dependent resources are available.
+ */
+
+ self->busy = TRUE;
+
+ g_signal_connect_object (task,
+ "notify::completed",
+ G_CALLBACK (ide_run_manager_task_completed),
+ self,
+ G_CONNECT_SWAPPED);
+
+ ide_build_manager_install_async (build_manager,
+ g_task_get_cancellable (task),
+ ide_run_manager_install_cb,
+ g_object_ref (task));
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUSY]);
+}
+
+static void
+ide_run_manager_run_discover_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeRunManager *self = (IdeRunManager *)object;
+ g_autoptr(IdeBuildTarget) build_target = NULL;
+ g_autoptr(GTask) task = user_data;
+ GError *error = NULL;
+
+ g_assert (IDE_IS_RUN_MANAGER (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+
+ build_target = ide_run_manager_discover_default_target_finish (self, result, &error);
+
+ if (build_target == NULL)
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ g_task_set_task_data (task, g_steal_pointer (&build_target), g_object_unref);
+
+ ide_run_manager_do_install_before_run (self, task);
+}
+
void
ide_run_manager_run_async (IdeRunManager *self,
IdeBuildTarget *build_target,
@@ -253,14 +395,12 @@ ide_run_manager_run_async (IdeRunManager *self,
{
g_autoptr(GTask) task = NULL;
g_autoptr(GCancellable) local_cancellable = NULL;
- IdeBuildManager *build_manager;
- IdeContext *context;
GError *error = NULL;
IDE_ENTRY;
g_return_if_fail (IDE_IS_RUN_MANAGER (self));
- g_return_if_fail (IDE_IS_BUILD_TARGET (build_target));
+ g_return_if_fail (!build_target || IDE_IS_BUILD_TARGET (build_target));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
if (cancellable == NULL)
@@ -268,40 +408,33 @@ ide_run_manager_run_async (IdeRunManager *self,
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, ide_run_manager_run_async);
- g_task_set_task_data (task, g_object_ref (build_target), g_object_unref);
+
+ g_set_object (&self->cancellable, cancellable);
if (ide_run_manager_check_busy (self, &error))
{
g_task_return_error (task, error);
- IDE_GOTO (failure);
+ IDE_EXIT;
}
- /*
- * First we need to make sure the target is up to date and installed
- * so that all the dependent resources are available.
- */
-
- context = ide_object_get_context (IDE_OBJECT (self));
- build_manager = ide_context_get_build_manager (context);
-
- self->busy = TRUE;
-
- g_set_object (&self->cancellable, cancellable);
-
- g_signal_connect_object (task,
- "notify::completed",
- G_CALLBACK (ide_run_manager_task_completed),
- self,
- G_CONNECT_SWAPPED);
+ if (build_target == NULL)
+ {
+ build_target = ide_run_manager_get_build_target (self);
+
+ if (build_target == NULL)
+ {
+ ide_run_manager_discover_default_target_async (self,
+ cancellable,
+ ide_run_manager_run_discover_cb,
+ g_object_ref (task));
+ IDE_EXIT;
+ }
+ }
- ide_build_manager_install_async (build_manager,
- cancellable,
- ide_run_manager_install_cb,
- g_steal_pointer (&task));
+ g_task_set_task_data (task, g_object_ref (build_target), g_object_unref);
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUSY]);
+ ide_run_manager_do_install_before_run (self, task);
-failure:
IDE_EXIT;
}
@@ -325,7 +458,7 @@ ide_run_manager_run_finish (IdeRunManager *self,
static gboolean
do_cancel_in_timeout (gpointer user_data)
{
- GCancellable *cancellable = user_data;
+ g_autoptr(GCancellable) cancellable = user_data;
IDE_ENTRY;
@@ -349,3 +482,243 @@ ide_run_manager_cancel (IdeRunManager *self)
IDE_EXIT;
}
+
+void
+ide_run_manager_set_handler (IdeRunManager *self,
+ const gchar *id)
+{
+ g_return_if_fail (IDE_IS_RUN_MANAGER (self));
+
+ self->handler = NULL;
+
+ for (GList *iter = self->handlers; iter; iter = iter->next)
+ {
+ const IdeRunHandlerInfo *info = iter->data;
+
+ if (g_strcmp0 (info->id, id) == 0)
+ {
+ self->handler = info;
+ IDE_TRACE_MSG ("run handler set to %s", info->title);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HANDLER]);
+ break;
+ }
+ }
+}
+
+void
+ide_run_manager_add_handler (IdeRunManager *self,
+ const gchar *id,
+ const gchar *title,
+ const gchar *icon_name,
+ const gchar *accel,
+ IdeRunHandler run_handler,
+ gpointer user_data,
+ GDestroyNotify user_data_destroy)
+{
+ IdeRunHandlerInfo *info;
+
+ g_return_if_fail (IDE_IS_RUN_MANAGER (self));
+ g_return_if_fail (id != NULL);
+ g_return_if_fail (title != NULL);
+
+ info = g_slice_new (IdeRunHandlerInfo);
+ info->id = g_strdup (id);
+ info->title = g_strdup (title);
+ info->icon_name = g_strdup (icon_name);
+ info->accel = g_strdup (accel);
+ info->handler = run_handler;
+ info->handler_data = user_data;
+ info->handler_data_destroy = user_data_destroy;
+
+ self->handlers = g_list_append (self->handlers, info);
+
+ if (self->handler == NULL)
+ self->handler = info;
+}
+
+void
+ide_run_manager_remove_handler (IdeRunManager *self,
+ const gchar *id)
+{
+ g_return_if_fail (IDE_IS_RUN_MANAGER (self));
+ g_return_if_fail (id != NULL);
+
+ for (GList *iter = self->handlers; iter; iter = iter->next)
+ {
+ IdeRunHandlerInfo *info = iter->data;
+
+ if (g_strcmp0 (info->id, id) == 0)
+ {
+ self->handlers = g_list_remove_link (self->handlers, iter);
+
+ if (self->handler == info && self->handlers != NULL)
+ self->handler = self->handlers->data;
+ else
+ self->handler = NULL;
+
+ ide_run_handler_info_free (info);
+
+ break;
+ }
+ }
+}
+
+/**
+ * ide_run_manager_get_build_target:
+ *
+ * Gets the build target that will be executed by the run manager if a
+ * specific build target has not been specified to ide_run_manager_run_async().
+ *
+ * Returns: (transfer none): An #IdeBuildTarget or %NULL if no build target
+ * has been set.
+ */
+IdeBuildTarget *
+ide_run_manager_get_build_target (IdeRunManager *self)
+{
+ g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
+
+ return self->build_target;
+}
+
+static IdeBuildTarget *
+find_best_target (GPtrArray *targets)
+{
+ IdeBuildTarget *ret = NULL;
+ guint i;
+
+ g_assert (targets != NULL);
+
+ /* TODO:
+ *
+ * This is just a barebones way to try to discover a target that matters. We
+ * could probably defer this off to the build system. Either way, it's shit
+ * and should be thought through by someone.
+ */
+
+ for (i = 0; i < targets->len; i++)
+ {
+ IdeBuildTarget *target = g_ptr_array_index (targets, i);
+ g_autoptr(GFile) installdir = NULL;
+
+ installdir = ide_build_target_get_install_directory (target);
+
+ if (installdir == NULL)
+ continue;
+
+ if (ret == NULL)
+ ret = target;
+ }
+
+ return ret;
+}
+
+static void
+ide_run_manager_discover_default_target_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeBuildSystem *build_system = (IdeBuildSystem *)object;
+ g_autoptr(GTask) task = user_data;
+ g_autoptr(GPtrArray) targets = NULL;
+ IdeBuildTarget *best_match;
+ GError *error = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_BUILD_SYSTEM (build_system));
+ g_assert (G_IS_ASYNC_RESULT (result));
+
+ targets = ide_build_system_get_build_targets_finish (build_system, result, &error);
+
+ if (targets == NULL)
+ {
+ g_task_return_error (task, error);
+ IDE_EXIT;
+ }
+
+ best_match = find_best_target (targets);
+
+ if (best_match == NULL)
+ {
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Failed to locate build target");
+ IDE_EXIT;
+ }
+
+ g_task_return_pointer (task, g_object_ref (best_match), g_object_unref);
+
+ IDE_EXIT;
+}
+
+void
+ide_run_manager_discover_default_target_async (IdeRunManager *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ IdeBuildSystem *build_system;
+ IdeContext *context;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_RUN_MANAGER (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, ide_run_manager_discover_default_target_async);
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+ build_system = ide_context_get_build_system (context);
+
+ ide_build_system_get_build_targets_async (build_system,
+ cancellable,
+ ide_run_manager_discover_default_target_cb,
+ g_object_ref (task));
+
+ IDE_EXIT;
+}
+
+/**
+ * ide_run_manager_discover_default_target_finish:
+ *
+ * Returns: (transfer none): An #IdeBuildTarget if successful; otherwise %NULL
+ * and @error is set.
+ */
+IdeBuildTarget *
+ide_run_manager_discover_default_target_finish (IdeRunManager *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ IdeBuildTarget *ret;
+
+ IDE_ENTRY;
+
+ g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
+ g_return_val_if_fail (G_IS_TASK (result), NULL);
+
+ ret = g_task_propagate_pointer (G_TASK (result), error);
+
+ IDE_RETURN (ret);
+}
+
+const GList *
+_ide_run_manager_get_handlers (IdeRunManager *self)
+{
+ g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
+
+ return self->handlers;
+}
+
+const gchar *
+ide_run_manager_get_handler (IdeRunManager *self)
+{
+ g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
+
+ if (self->handler != NULL)
+ return self->handler->id;
+
+ return NULL;
+}
diff --git a/libide/runner/ide-run-manager.h b/libide/runner/ide-run-manager.h
index 49a3e3f..81356f8 100644
--- a/libide/runner/ide-run-manager.h
+++ b/libide/runner/ide-run-manager.h
@@ -21,22 +21,50 @@
#include "ide-object.h"
+#include "buildsystem/ide-build-target.h"
+#include "runner/ide-runner.h"
+
G_BEGIN_DECLS
#define IDE_TYPE_RUN_MANAGER (ide_run_manager_get_type())
G_DECLARE_FINAL_TYPE (IdeRunManager, ide_run_manager, IDE, RUN_MANAGER, IdeObject)
-void ide_run_manager_cancel (IdeRunManager *self);
-gboolean ide_run_manager_get_busy (IdeRunManager *self);
-void ide_run_manager_run_async (IdeRunManager *self,
- IdeBuildTarget *build_target,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gboolean ide_run_manager_run_finish (IdeRunManager *self,
- GAsyncResult *result,
- GError **error);
+typedef void (*IdeRunHandler) (IdeRunManager *self,
+ IdeRunner *runner,
+ gpointer user_data);
+
+IdeBuildTarget *ide_run_manager_get_build_target (IdeRunManager *self);
+void ide_run_manager_cancel (IdeRunManager *self);
+gboolean ide_run_manager_get_busy (IdeRunManager *self);
+const gchar *ide_run_manager_get_handler (IdeRunManager *self);
+void ide_run_manager_set_handler (IdeRunManager *self,
+ const gchar *id);
+void ide_run_manager_add_handler (IdeRunManager *self,
+ const gchar *id,
+ const gchar *title,
+ const gchar *icon_name,
+ const gchar *accel,
+ IdeRunHandler run_handler,
+ gpointer user_data,
+ GDestroyNotify user_data_destroy);
+void ide_run_manager_remove_handler (IdeRunManager *self,
+ const gchar *id);
+void ide_run_manager_run_async (IdeRunManager *self,
+ IdeBuildTarget *build_target,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean ide_run_manager_run_finish (IdeRunManager *self,
+ GAsyncResult *result,
+ GError **error);
+void ide_run_manager_discover_default_target_async (IdeRunManager *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IdeBuildTarget *ide_run_manager_discover_default_target_finish (IdeRunManager *self,
+ GAsyncResult *result,
+ GError **error);
G_END_DECLS
diff --git a/libide/runner/ide-runner.c b/libide/runner/ide-runner.c
index 8b4f1e0..eb26208 100644
--- a/libide/runner/ide-runner.c
+++ b/libide/runner/ide-runner.c
@@ -47,6 +47,11 @@ enum {
N_PROPS
};
+enum {
+ SPAWNED,
+ N_SIGNALS
+};
+
static void ide_runner_tick_posthook (GTask *task);
static void ide_runner_tick_prehook (GTask *task);
static void ide_runner_tick_run (GTask *task);
@@ -54,6 +59,7 @@ static void ide_runner_tick_run (GTask *task);
G_DEFINE_TYPE_WITH_PRIVATE (IdeRunner, ide_runner, IDE_TYPE_OBJECT)
static GParamSpec *properties [N_PROPS];
+static guint signals [N_SIGNALS];
static IdeRunnerAddin *
pop_runner_addin (GSList **list)
@@ -135,6 +141,7 @@ ide_runner_real_run_async (IdeRunner *self,
g_autoptr(GTask) task = NULL;
g_autoptr(IdeSubprocessLauncher) launcher = NULL;
g_autoptr(GSubprocess) subprocess = NULL;
+ const gchar *identifier;
GError *error = NULL;
IDE_ENTRY;
@@ -162,6 +169,10 @@ ide_runner_real_run_async (IdeRunner *self,
IDE_GOTO (failure);
}
+ identifier = g_subprocess_get_identifier (subprocess);
+
+ g_signal_emit (self, signals [SPAWNED], 0, identifier);
+
g_subprocess_wait_async (subprocess,
cancellable,
ide_runner_run_wait_cb,
@@ -316,6 +327,18 @@ ide_runner_class_init (IdeRunnerClass *klass)
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ signals [SPAWNED] =
+ g_signal_new ("spawned",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
}
static void
diff --git a/libide/workbench/ide-perspective-menu-button.ui b/libide/workbench/ide-perspective-menu-button.ui
index 0c24c0b..cb0f161 100644
--- a/libide/workbench/ide-perspective-menu-button.ui
+++ b/libide/workbench/ide-perspective-menu-button.ui
@@ -2,7 +2,7 @@
<interface>
<object class="GtkPopover" id="popover">
<style>
- <class name="perspectives-selector"/>
+ <class name="popover-selector"/>
</style>
<child>
<object class="GtkListBox" id="list_box">
diff --git a/libide/workbench/ide-workbench-header-bar.ui b/libide/workbench/ide-workbench-header-bar.ui
index 87ac0eb..4d4b0e4 100644
--- a/libide/workbench/ide-workbench-header-bar.ui
+++ b/libide/workbench/ide-workbench-header-bar.ui
@@ -25,6 +25,15 @@
<property name="spacing">30</property>
<property name="visible">true</property>
<child>
+ <object class="IdeRunButton">
+ <property name="visible">true</property>
+ </object>
+ <packing>
+ <property name="priority">0</property>
+ <property name="pack-type">start</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkMenuButton" id="menu_button">
<property name="tooltip-text" translatable="yes">Show workbench menu</property>
<property name="focus-on-click">false</property>
diff --git a/libide/workers/ide-subprocess-launcher.c b/libide/workers/ide-subprocess-launcher.c
index ba37a9d..44e6563 100644
--- a/libide/workers/ide-subprocess-launcher.c
+++ b/libide/workers/ide-subprocess-launcher.c
@@ -180,6 +180,11 @@ ide_subprocess_launcher_real_spawn_async (IdeSubprocessLauncher *self,
g_assert (IDE_IS_SUBPROCESS_LAUNCHER (self));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+ /*
+ * TODO: Adding threads here doesn't really make things any easier.
+ * We should just spawn synchronously in the current thread.
+ */
+
task = g_task_new (self, cancellable, callback, user_data);
g_task_run_in_thread (task, ide_subprocess_launcher_spawn_worker);
}
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 0827d68..14b3b4b 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -31,6 +31,7 @@ SUBDIRS = \
support \
symbol-tree \
sysmon \
+ sysprof \
terminal \
todo \
vala-pack \
diff --git a/plugins/autotools/ide-makecache.c b/plugins/autotools/ide-makecache.c
index f06c9a9..765ab08 100644
--- a/plugins/autotools/ide-makecache.c
+++ b/plugins/autotools/ide-makecache.c
@@ -1916,6 +1916,7 @@ ide_makecache_get_build_targets_worker (GTask *task,
ide_subprocess_launcher_push_argv (launcher, "print-bindir");
ide_subprocess_launcher_push_argv (launcher, "print-libexecdir");
ide_subprocess_launcher_push_argv (launcher, "print-bin_PROGRAMS");
+ ide_subprocess_launcher_push_argv (launcher, "print-bin_SCRIPTS");
ide_subprocess_launcher_push_argv (launcher, "print-noinst_PROGRAMS");
ide_subprocess_launcher_push_argv (launcher, "print-libexec_PROGRAMS");
diff --git a/plugins/run-tools/gbp-run-workbench-addin.c b/plugins/run-tools/gbp-run-workbench-addin.c
index 6cf8fd6..5cdb2c4 100644
--- a/plugins/run-tools/gbp-run-workbench-addin.c
+++ b/plugins/run-tools/gbp-run-workbench-addin.c
@@ -61,93 +61,6 @@ failure:
IDE_EXIT;
}
-static IdeBuildTarget *
-find_best_target (GPtrArray *targets)
-{
- IdeBuildTarget *ret = NULL;
- guint i;
-
- g_assert (targets != NULL);
-
- for (i = 0; i < targets->len; i++)
- {
- IdeBuildTarget *target = g_ptr_array_index (targets, i);
- g_autoptr(GFile) installdir = NULL;
-
- installdir = ide_build_target_get_install_directory (target);
-
- if (installdir == NULL)
- continue;
-
- if (ret == NULL)
- ret = target;
-
- /* TODO: Compare likelyhood of primary binary */
- }
-
- return ret;
-}
-
-static void
-gbp_run_workbench_addin_get_build_targets_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
-{
- IdeBuildSystem *build_system = (IdeBuildSystem *)object;
- GbpRunWorkbenchAddin *self;
- g_autoptr(GPtrArray) targets = NULL;
- g_autoptr(GTask) task = user_data;
- IdeBuildTarget *best_match;
- IdeRunManager *run_manager;
- GCancellable *cancellable;
- IdeContext *context;
- GError *error = NULL;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_BUILD_SYSTEM (build_system));
- g_assert (G_IS_ASYNC_RESULT (result));
- g_assert (G_IS_TASK (task));
-
- self = g_task_get_source_object (task);
- cancellable = g_task_get_cancellable (task);
-
- g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
- g_assert (GBP_IS_RUN_WORKBENCH_ADDIN (self));
- g_assert (IDE_IS_WORKBENCH (self->workbench));
-
- targets = ide_build_system_get_build_targets_finish (build_system, result, &error);
-
- if (targets == NULL)
- {
- g_task_return_error (task, error);
- IDE_EXIT;
- }
-
- best_match = find_best_target (targets);
-
- if (best_match == NULL)
- {
- g_task_return_new_error (task,
- G_IO_ERROR,
- G_IO_ERROR_FAILED,
- "%s",
- _("Failed to locate build target"));
- IDE_EXIT;
- }
-
- context = ide_workbench_get_context (self->workbench);
- run_manager = ide_context_get_run_manager (context);
-
- ide_run_manager_run_async (run_manager,
- best_match,
- cancellable,
- gbp_run_workbench_addin_run_cb,
- g_steal_pointer (&task));
-
- IDE_EXIT;
-}
-
static void
gbp_run_workbench_addin_run (GSimpleAction *action,
GVariant *param,
@@ -155,7 +68,7 @@ gbp_run_workbench_addin_run (GSimpleAction *action,
{
GbpRunWorkbenchAddin *self = user_data;
g_autoptr(GTask) task = NULL;
- IdeBuildSystem *build_system;
+ IdeRunManager *run_manager;
IdeContext *context;
IDE_ENTRY;
@@ -164,15 +77,16 @@ gbp_run_workbench_addin_run (GSimpleAction *action,
g_assert (GBP_IS_RUN_WORKBENCH_ADDIN (self));
context = ide_workbench_get_context (self->workbench);
- build_system = ide_context_get_build_system (context);
+ run_manager = ide_context_get_run_manager (context);
task = g_task_new (self, NULL, NULL, NULL);
g_task_set_source_tag (task, gbp_run_workbench_addin_run);
- ide_build_system_get_build_targets_async (build_system,
- NULL,
- gbp_run_workbench_addin_get_build_targets_cb,
- g_steal_pointer (&task));
+ ide_run_manager_run_async (run_manager,
+ NULL,
+ NULL,
+ gbp_run_workbench_addin_run_cb,
+ g_steal_pointer (&task));
IDE_EXIT;
}
@@ -205,10 +119,8 @@ gbp_run_workbench_addin_load (IdeWorkbenchAddin *addin,
{
GbpRunWorkbenchAddin *self = (GbpRunWorkbenchAddin *)addin;
g_autoptr(GSimpleActionGroup) group = NULL;
- IdeWorkbenchHeaderBar *headerbar;
IdeRunManager *run_manager;
IdeContext *context;
- GtkWidget *button;
static const GActionEntry entries[] = {
{ "run", gbp_run_workbench_addin_run },
{ "stop", gbp_run_workbench_addin_stop },
@@ -222,33 +134,6 @@ gbp_run_workbench_addin_load (IdeWorkbenchAddin *addin,
context = ide_workbench_get_context (workbench);
run_manager = ide_context_get_run_manager (context);
- headerbar = ide_workbench_get_headerbar (workbench);
-
- button = g_object_new (GTK_TYPE_BUTTON,
- "action-name", "run-tools.run",
- "focus-on-click", FALSE,
- "child", g_object_new (GTK_TYPE_IMAGE,
- "icon-name", "media-playback-start-symbolic",
- "visible", TRUE,
- NULL),
- "tooltip-text", _("Run project"),
- NULL);
- g_object_bind_property (run_manager, "busy", button, "visible", G_BINDING_SYNC_CREATE |
G_BINDING_INVERT_BOOLEAN);
- ide_widget_add_style_class (button, "image-button");
- ide_workbench_header_bar_insert_right (headerbar, button, GTK_PACK_START, 0);
-
- button = g_object_new (GTK_TYPE_BUTTON,
- "action-name", "run-tools.stop",
- "focus-on-click", FALSE,
- "child", g_object_new (GTK_TYPE_IMAGE,
- "icon-name", "media-playback-stop-symbolic",
- "visible", TRUE,
- NULL),
- NULL);
- g_object_bind_property (run_manager, "busy", button, "visible", G_BINDING_SYNC_CREATE);
- ide_widget_add_style_class (button, "image-button");
- ide_workbench_header_bar_insert_right (headerbar, button, GTK_PACK_START, 0);
-
group = g_simple_action_group_new ();
g_action_map_add_action_entries (G_ACTION_MAP (group), entries, G_N_ELEMENTS (entries), self);
gtk_widget_insert_action_group (GTK_WIDGET (workbench), "run-tools", G_ACTION_GROUP (group));
diff --git a/plugins/sysprof/Makefile.am b/plugins/sysprof/Makefile.am
new file mode 100644
index 0000000..27bc552
--- /dev/null
+++ b/plugins/sysprof/Makefile.am
@@ -0,0 +1,37 @@
+if ENABLE_SYSPROF_PLUGIN
+
+DISTCLEANFILES =
+BUILT_SOURCES =
+CLEANFILES =
+EXTRA_DIST = $(plugin_DATA)
+
+plugindir = $(libdir)/gnome-builder/plugins
+plugin_LTLIBRARIES = libsysprof-plugin.la
+dist_plugin_DATA = sysprof.plugin
+
+libsysprof_plugin_la_SOURCES = \
+ gbp-sysprof-plugin.c \
+ gbp-sysprof-perspective.c \
+ gbp-sysprof-perspective.h \
+ gbp-sysprof-workbench-addin.c \
+ gbp-sysprof-workbench-addin.h \
+ $(NULL)
+
+nodist_libsysprof_plugin_la_SOURCES = \
+ gbp-sysprof-resources.c \
+ gbp-sysprof-resources.h
+
+libsysprof_plugin_la_CFLAGS = $(PLUGIN_CFLAGS) $(SYSPROF_CFLAGS)
+libsysprof_plugin_la_LIBADD = $(PLUGIN_LIBS) $(SYSPROF_LIBS)
+
+glib_resources_c = gbp-sysprof-resources.c
+glib_resources_h = gbp-sysprof-resources.h
+glib_resources_xml = sysprof.gresource.xml
+glib_resources_namespace = gbp_sysprof
+include $(top_srcdir)/build/autotools/Makefile.am.gresources
+
+include $(top_srcdir)/plugins/Makefile.plugin
+
+endif
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/sysprof/configure.ac b/plugins/sysprof/configure.ac
new file mode 100644
index 0000000..657d385
--- /dev/null
+++ b/plugins/sysprof/configure.ac
@@ -0,0 +1,21 @@
+m4_define(sysprof_required_version, [3.20.1])
+
+PKG_CHECK_MODULES(SYSPROF, [sysprof-2 >= sysprof_required_version
+ sysprof-ui-2 >= sysprof_required_version])
+
+# --enable-sysprof-plugin=yes/no
+AC_ARG_ENABLE([sysprof-plugin],
+ [AS_HELP_STRING([--enable-sysprof-plugin=@<:@yes/no@:>@],
+ [Build with support for the Sysprof system profiler.])],
+ [enable_sysprof_plugin=$enableval],
+ [enable_sysprof_plugin=yes])
+
+AS_IF([test x$enable_sysprof_plugin = xyes && test x$have_sysprof_ui = xno],[
+ AC_MSG_ERROR([Failed to locate sysprof-ui dependencies. Try installing Sysprof with GTK support.])
+])
+
+# for if ENABLE_SYSPROF_PLUGIN in Makefile.am
+AM_CONDITIONAL(ENABLE_SYSPROF_PLUGIN, test x$enable_sysprof_plugin != xno)
+
+# Ensure our makefile is generated by autoconf
+AC_CONFIG_FILES([plugins/sysprof/Makefile])
diff --git a/plugins/sysprof/gbp-sysprof-perspective.c b/plugins/sysprof/gbp-sysprof-perspective.c
new file mode 100644
index 0000000..3928954
--- /dev/null
+++ b/plugins/sysprof/gbp-sysprof-perspective.c
@@ -0,0 +1,184 @@
+/* gbp-sysprof-perspective.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gbp-sysprof-perspective"
+
+#include <glib/gi18n.h>
+#include <sysprof.h>
+#include <sysprof-ui.h>
+
+#include "gbp-sysprof-perspective.h"
+
+struct _GbpSysprofPerspective
+{
+ GtkBin parent_instance;
+
+ SpCallgraphView *callgraph_view;
+};
+
+enum {
+ PROP_0,
+ N_PROPS
+};
+
+static void perspective_iface_init (IdePerspectiveInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GbpSysprofPerspective, gbp_sysprof_perspective, GTK_TYPE_BIN, 0,
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_PERSPECTIVE, perspective_iface_init))
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+gbp_sysprof_perspective_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (gbp_sysprof_perspective_parent_class)->finalize (object);
+}
+
+static void
+gbp_sysprof_perspective_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GbpSysprofPerspective *self = GBP_SYSPROF_PERSPECTIVE (object);
+
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gbp_sysprof_perspective_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GbpSysprofPerspective *self = GBP_SYSPROF_PERSPECTIVE (object);
+
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gbp_sysprof_perspective_class_init (GbpSysprofPerspectiveClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = gbp_sysprof_perspective_finalize;
+ object_class->get_property = gbp_sysprof_perspective_get_property;
+ object_class->set_property = gbp_sysprof_perspective_set_property;
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/builder/plugins/sysprof-plugin/gbp-sysprof-perspective.ui");
+ gtk_widget_class_bind_template_child (widget_class, GbpSysprofPerspective, callgraph_view);
+
+ g_type_ensure (SP_TYPE_CALLGRAPH_VIEW);
+}
+
+static void
+gbp_sysprof_perspective_init (GbpSysprofPerspective *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+static gchar *
+gbp_sysprof_perspective_get_icon_name (IdePerspective *perspective)
+{
+ return g_strdup ("utilities-system-monitor-symbolic");
+}
+
+static gchar *
+gbp_sysprof_perspective_get_title (IdePerspective *perspective)
+{
+ return g_strdup (_("Profiler"));
+}
+
+static gchar *
+gbp_sysprof_perspective_get_id (IdePerspective *perspective)
+{
+ return g_strdup ("profiler");
+}
+
+static gint
+gbp_sysprof_perspective_get_priority (IdePerspective *perspective)
+{
+ return 70000;
+}
+
+static gchar *
+gbp_sysprof_perspective_get_accelerator (IdePerspective *perspective)
+{
+ return g_strdup ("<Alt>2");
+}
+
+static void
+perspective_iface_init (IdePerspectiveInterface *iface)
+{
+ iface->get_icon_name = gbp_sysprof_perspective_get_icon_name;
+ iface->get_title = gbp_sysprof_perspective_get_title;
+ iface->get_id = gbp_sysprof_perspective_get_id;
+ iface->get_priority = gbp_sysprof_perspective_get_priority;
+ iface->get_accelerator = gbp_sysprof_perspective_get_accelerator;
+}
+
+static void
+generate_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ SpCallgraphProfile *profile = (SpCallgraphProfile *)object;
+ g_autoptr(GbpSysprofPerspective) self = user_data;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (SP_IS_CALLGRAPH_PROFILE (profile));
+ g_assert (GBP_IS_SYSPROF_PERSPECTIVE (self));
+
+ if (!sp_profile_generate_finish (SP_PROFILE (profile), result, &error))
+ {
+ g_warning ("Failed to generate profile: %s", error->message);
+ return;
+ }
+
+ sp_callgraph_view_set_profile (self->callgraph_view, profile);
+}
+
+void
+gbp_sysprof_perspective_set_reader (GbpSysprofPerspective *self,
+ SpCaptureReader *reader)
+{
+ g_autoptr(SpProfile) profile = NULL;
+
+ g_assert (GBP_IS_SYSPROF_PERSPECTIVE (self));
+
+ if (reader == NULL)
+ {
+ sp_callgraph_view_set_profile (self->callgraph_view, NULL);
+ return;
+ }
+
+ profile = sp_callgraph_profile_new ();
+
+ sp_profile_set_reader (profile, reader);
+
+ sp_profile_generate (profile, NULL, generate_cb, g_object_ref (self));
+}
diff --git a/plugins/sysprof/gbp-sysprof-perspective.h b/plugins/sysprof/gbp-sysprof-perspective.h
new file mode 100644
index 0000000..54355c4
--- /dev/null
+++ b/plugins/sysprof/gbp-sysprof-perspective.h
@@ -0,0 +1,36 @@
+/* gbp-sysprof-perspective.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GBP_SYSPROF_PERSPECTIVE_H
+#define GBP_SYSPROF_PERSPECTIVE_H
+
+#include <ide.h>
+#include <sysprof.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_SYSPROF_PERSPECTIVE (gbp_sysprof_perspective_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpSysprofPerspective, gbp_sysprof_perspective, GBP, SYSPROF_PERSPECTIVE, GtkBin)
+
+void gbp_sysprof_perspective_set_reader (GbpSysprofPerspective *self,
+ SpCaptureReader *reader);
+
+G_END_DECLS
+
+#endif /* GBP_SYSPROF_PERSPECTIVE_H */
diff --git a/plugins/sysprof/gbp-sysprof-perspective.ui b/plugins/sysprof/gbp-sysprof-perspective.ui
new file mode 100644
index 0000000..253f51b
--- /dev/null
+++ b/plugins/sysprof/gbp-sysprof-perspective.ui
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GbpSysprofPerspective" parent="GtkBin">
+ <child>
+ <object class="GtkStack">
+ <property name="visible">true</property>
+ <child>
+ <object class="SpCallgraphView" id="callgraph_view">
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/plugins/sysprof/gbp-sysprof-plugin.c b/plugins/sysprof/gbp-sysprof-plugin.c
new file mode 100644
index 0000000..987ff07
--- /dev/null
+++ b/plugins/sysprof/gbp-sysprof-plugin.c
@@ -0,0 +1,30 @@
+/* gbp-sysprof-plugin.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libpeas/peas.h>
+#include <ide.h>
+
+#include "gbp-sysprof-workbench-addin.h"
+
+void
+peas_register_types (PeasObjectModule *module)
+{
+ peas_object_module_register_extension_type (module,
+ IDE_TYPE_WORKBENCH_ADDIN,
+ GBP_TYPE_SYSPROF_WORKBENCH_ADDIN);
+}
diff --git a/plugins/sysprof/gbp-sysprof-workbench-addin.c b/plugins/sysprof/gbp-sysprof-workbench-addin.c
new file mode 100644
index 0000000..f0dd876
--- /dev/null
+++ b/plugins/sysprof/gbp-sysprof-workbench-addin.c
@@ -0,0 +1,460 @@
+/* gbp-sysprof-workbench-addin.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+#include <sysprof.h>
+
+#include "gbp-sysprof-perspective.h"
+#include "gbp-sysprof-workbench-addin.h"
+
+struct _GbpSysprofWorkbenchAddin
+{
+ GObject parent_instance;
+
+ GSimpleActionGroup *actions;
+ SpProfiler *profiler;
+
+ GbpSysprofPerspective *perspective;
+ IdeWorkbench *workbench;
+};
+
+static void workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GbpSysprofWorkbenchAddin, gbp_sysprof_workbench_addin, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_WORKBENCH_ADDIN, workbench_addin_iface_init))
+
+static void
+set_action_state (GbpSysprofWorkbenchAddin *self,
+ const gchar *action_name,
+ GVariant *state,
+ gboolean enabled)
+{
+ GAction *action;
+
+ g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+ g_assert (action_name != NULL);
+
+ action = g_action_map_lookup_action (G_ACTION_MAP (self->actions), action_name);
+ g_simple_action_set_state (G_SIMPLE_ACTION (action), state);
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enabled);
+}
+
+static void
+profiler_stopped (GbpSysprofWorkbenchAddin *self,
+ SpProfiler *profiler)
+{
+ g_autoptr(SpCaptureReader) reader = NULL;
+ g_autoptr(GError) error = NULL;
+ SpCaptureWriter *writer;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+ g_return_if_fail (SP_IS_PROFILER (profiler));
+
+ if (self->profiler != profiler)
+ IDE_EXIT;
+
+ set_action_state (self, "start-profiler", g_variant_new_boolean (FALSE), TRUE);
+
+ writer = sp_profiler_get_writer (profiler);
+ reader = sp_capture_writer_create_reader (writer, &error);
+
+ if (reader == NULL)
+ {
+ /* TODO: Propagate error to an infobar or similar */
+ g_warning ("%s", error->message);
+ IDE_EXIT;
+ }
+
+ gbp_sysprof_perspective_set_reader (self->perspective, reader);
+
+ IDE_EXIT;
+}
+
+static void
+profiler_child_spawned (GbpSysprofWorkbenchAddin *self,
+ const gchar *identifier,
+ IdeRunner *runner)
+{
+ GPid pid = 0;
+
+ g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+ g_assert (identifier != NULL);
+ g_assert (IDE_IS_RUNNER (runner));
+
+#ifdef G_OS_UNIX
+ pid = g_ascii_strtoll (identifier, NULL, 10);
+#endif
+
+ if G_UNLIKELY (pid == 0)
+ {
+ g_warning ("Failed to parse integer value from %s", identifier);
+ return;
+ }
+
+ sp_profiler_add_pid (self->profiler, pid);
+ sp_profiler_start (self->profiler);
+}
+
+static void
+profiler_run_handler (IdeRunManager *run_manager,
+ IdeRunner *runner,
+ gpointer user_data)
+{
+ GbpSysprofWorkbenchAddin *self = user_data;
+
+ g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+ g_assert (IDE_IS_RUNNER (runner));
+ g_assert (IDE_IS_RUN_MANAGER (run_manager));
+
+ /*
+ * TODO:
+ *
+ * We need to synchronize the inferior with the parent here. Ideally, we would
+ * prepend the application launch (to some degree) with the application we want
+ * to execute. In this case, we might want to add a "gnome-builder-sysprof"
+ * helper that will synchronize with the parent, and then block until we start
+ * the process (with the appropriate pid) before exec() otherwise we could
+ * miss the exit of the app and race to add the pid to the profiler.
+ */
+
+ g_signal_connect_object (runner,
+ "spawned",
+ G_CALLBACK (profiler_child_spawned),
+ self,
+ G_CONNECT_SWAPPED);
+}
+
+
+static void
+gbp_sysprof_workbench_addin_start (GbpSysprofWorkbenchAddin *self)
+{
+ IdeContext *context;
+ IdeRunManager *run_manager;
+ g_autoptr(SpSource) proc_source = NULL;
+ g_autoptr(SpSource) perf_source = NULL;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+ g_return_if_fail (IDE_IS_PERSPECTIVE (self->perspective));
+ g_return_if_fail (IDE_IS_WORKBENCH (self->workbench));
+
+ if (self->profiler != NULL)
+ {
+ if (sp_profiler_get_is_running (self->profiler))
+ {
+ g_warning ("Profiler is already running, cannot start profiler.");
+ IDE_EXIT;
+ }
+ }
+
+ context = ide_workbench_get_context (self->workbench);
+
+ run_manager = ide_context_get_run_manager (context);
+ ide_run_manager_set_handler (run_manager, "profiler");
+
+ ide_workbench_set_visible_perspective (self->workbench, IDE_PERSPECTIVE (self->perspective));
+
+ set_action_state (self, "start-profiler", g_variant_new_boolean (TRUE), FALSE);
+
+ g_clear_object (&self->profiler);
+ self->profiler = sp_local_profiler_new ();
+
+ proc_source = sp_proc_source_new ();
+ sp_profiler_add_source (self->profiler, proc_source);
+
+ perf_source = sp_perf_source_new ();
+ sp_profiler_add_source (self->profiler, perf_source);
+
+ g_signal_connect_object (self->profiler,
+ "stopped",
+ G_CALLBACK (profiler_stopped),
+ self,
+ G_CONNECT_SWAPPED);
+
+ ide_run_manager_run_async (run_manager, NULL, NULL, NULL, NULL);
+
+ sp_profiler_start (self->profiler);
+
+ IDE_EXIT;
+}
+
+static void
+start_profiler_action (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ GbpSysprofWorkbenchAddin *self = user_data;
+
+ g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+
+ gbp_sysprof_workbench_addin_start (self);
+}
+
+static void
+gbp_sysprof_workbench_addin_open_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GbpSysprofWorkbenchAddin *self = (GbpSysprofWorkbenchAddin *)object;
+ g_autoptr(SpCaptureReader) reader = NULL;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+ g_assert (G_IS_TASK (result));
+
+ reader = g_task_propagate_pointer (G_TASK (result), &error);
+
+ g_assert (reader || error != NULL);
+
+ if (reader == NULL)
+ {
+ g_message ("%s", error->message);
+ return;
+ }
+
+ gbp_sysprof_perspective_set_reader (self->perspective, reader);
+}
+
+static void
+gbp_sysprof_workbench_addin_open_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GbpSysprofWorkbenchAddin *self = source_object;
+ g_autofree gchar *path = NULL;
+ SpCaptureReader *reader;
+ GFile *file = task_data;
+ GError *error = NULL;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+ g_assert (G_IS_FILE (file));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ path = g_file_get_path (file);
+
+ if (NULL == (reader = sp_capture_reader_new (path, &error)))
+ {
+ g_assert (error != NULL);
+ g_task_return_error (task, error);
+ return;
+ }
+
+ g_task_return_pointer (task, reader, (GDestroyNotify)sp_capture_reader_unref);
+}
+
+static void
+gbp_sysprof_workbench_addin_open (GbpSysprofWorkbenchAddin *self,
+ GFile *file)
+{
+ g_autoptr(GTask) task = NULL;
+
+ g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+ g_assert (G_IS_FILE (file));
+
+ if (!g_file_is_native (file))
+ {
+ g_warning ("Can only open local sysprof capture files.");
+ return;
+ }
+
+ task = g_task_new (self, NULL, gbp_sysprof_workbench_addin_open_cb, NULL);
+ g_task_set_task_data (task, g_object_ref (file), g_object_unref);
+ g_task_run_in_thread (task, gbp_sysprof_workbench_addin_open_worker);
+}
+
+static void
+open_profile_action (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ GbpSysprofWorkbenchAddin *self = user_data;
+ GtkFileChooserNative *native;
+ GtkFileFilter *filter;
+ gint ret;
+
+ g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+ g_assert (IDE_IS_WORKBENCH (self->workbench));
+ g_assert (GBP_IS_SYSPROF_PERSPECTIVE (self->perspective));
+
+ ide_workbench_set_visible_perspective (self->workbench, IDE_PERSPECTIVE (self->perspective));
+
+ native = gtk_file_chooser_native_new (_("Open Profile"),
+ GTK_WINDOW (self->workbench),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ _("Open"),
+ _("Cancel"));
+
+ /* Add our filter for sysprof capture files. */
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, _("Sysprof Capture (*.syscap)"));
+ gtk_file_filter_add_pattern (filter, "*.syscap");
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (native), filter);
+
+ /* And all files now */
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, _("All Files"));
+ gtk_file_filter_add_pattern (filter, "*");
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (native), filter);
+
+ /* Unlike gtk_dialog_run(), this will handle processing
+ * various I/O events and so should be safe to use.
+ */
+ ret = gtk_native_dialog_run (GTK_NATIVE_DIALOG (native));
+
+ if (ret == GTK_RESPONSE_ACCEPT)
+ {
+ g_autoptr(GFile) file = NULL;
+
+ file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (native));
+ if (G_IS_FILE (file))
+ gbp_sysprof_workbench_addin_open (self, file);
+ }
+
+ gtk_native_dialog_hide (GTK_NATIVE_DIALOG (native));
+ gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (native));
+}
+
+static void
+gbp_sysprof_workbench_addin_finalize (GObject *object)
+{
+ GbpSysprofWorkbenchAddin *self = (GbpSysprofWorkbenchAddin *)object;
+
+ g_clear_object (&self->actions);
+
+ G_OBJECT_CLASS (gbp_sysprof_workbench_addin_parent_class)->finalize (object);
+}
+
+static void
+gbp_sysprof_workbench_addin_class_init (GbpSysprofWorkbenchAddinClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gbp_sysprof_workbench_addin_finalize;
+}
+
+static void
+gbp_sysprof_workbench_addin_init (GbpSysprofWorkbenchAddin *self)
+{
+ static const GActionEntry entries[] = {
+ { "start-profiler", start_profiler_action, NULL, "false" },
+ { "open-profile", open_profile_action },
+ };
+
+ self->actions = g_simple_action_group_new ();
+
+ g_action_map_add_action_entries (G_ACTION_MAP (self->actions),
+ entries,
+ G_N_ELEMENTS (entries),
+ self);
+}
+
+static void
+run_manager_stopped (GbpSysprofWorkbenchAddin *self,
+ IdeRunManager *run_manager)
+{
+ g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+ g_assert (IDE_IS_RUN_MANAGER (run_manager));
+
+ if (self->profiler != NULL && sp_profiler_get_is_running (self->profiler))
+ sp_profiler_stop (self->profiler);
+}
+
+static void
+gbp_sysprof_workbench_addin_load (IdeWorkbenchAddin *addin,
+ IdeWorkbench *workbench)
+{
+ GbpSysprofWorkbenchAddin *self = (GbpSysprofWorkbenchAddin *)addin;
+ IdeRunManager *run_manager;
+ IdeContext *context;
+
+ g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+ g_assert (IDE_IS_WORKBENCH (workbench));
+
+ self->workbench = workbench;
+
+ context = ide_workbench_get_context (workbench);
+
+ /*
+ * Register our custom run handler to activate the profiler.
+ */
+ run_manager = ide_context_get_run_manager (context);
+ ide_run_manager_add_handler (run_manager,
+ "profiler",
+ _("Profile"),
+ "utilities-system-monitor-symbolic",
+ "<Control>F8",
+ profiler_run_handler,
+ self,
+ NULL);
+ g_signal_connect_object (run_manager,
+ "stopped",
+ G_CALLBACK (run_manager_stopped),
+ self,
+ G_CONNECT_SWAPPED);
+
+ /*
+ * Add the perspcetive to the workbench.
+ */
+ self->perspective = g_object_new (GBP_TYPE_SYSPROF_PERSPECTIVE,
+ "visible", TRUE,
+ NULL);
+ ide_workbench_add_perspective (workbench, IDE_PERSPECTIVE (self->perspective));
+
+
+ /*
+ * Add our actions to the workbench so they can be activated via the
+ * headerbar or the perspective.
+ */
+ gtk_widget_insert_action_group (GTK_WIDGET (workbench),
+ "profiler",
+ G_ACTION_GROUP (self->actions));
+}
+
+static void
+gbp_sysprof_workbench_addin_unload (IdeWorkbenchAddin *addin,
+ IdeWorkbench *workbench)
+{
+ GbpSysprofWorkbenchAddin *self = (GbpSysprofWorkbenchAddin *)addin;
+ IdeRunManager *run_manager;
+ IdeContext *context;
+
+ g_assert (GBP_IS_SYSPROF_WORKBENCH_ADDIN (self));
+ g_assert (IDE_IS_WORKBENCH (workbench));
+
+ context = ide_workbench_get_context (workbench);
+
+ run_manager = ide_context_get_run_manager (context);
+ ide_run_manager_remove_handler (run_manager, "profiler");
+
+ ide_workbench_remove_perspective (workbench, IDE_PERSPECTIVE (self->perspective));
+
+ self->perspective = NULL;
+ self->workbench = NULL;
+}
+
+static void
+workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface)
+{
+ iface->load = gbp_sysprof_workbench_addin_load;
+ iface->unload = gbp_sysprof_workbench_addin_unload;
+}
diff --git a/plugins/sysprof/gbp-sysprof-workbench-addin.h b/plugins/sysprof/gbp-sysprof-workbench-addin.h
new file mode 100644
index 0000000..3df3d93
--- /dev/null
+++ b/plugins/sysprof/gbp-sysprof-workbench-addin.h
@@ -0,0 +1,33 @@
+/* gbp-sysprof-workbench-addin.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GBP_SYSPROF_WORKBENCH_ADDIN_H
+#define GBP_SYSPROF_WORKBENCH_ADDIN_H
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_SYSPROF_WORKBENCH_ADDIN (gbp_sysprof_workbench_addin_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpSysprofWorkbenchAddin, gbp_sysprof_workbench_addin, GBP, SYSPROF_WORKBENCH_ADDIN,
GObject)
+
+G_END_DECLS
+
+#endif /* GBP_SYSPROF_WORKBENCH_ADDIN_H */
+
diff --git a/plugins/sysprof/gtk/menus.ui b/plugins/sysprof/gtk/menus.ui
new file mode 100644
index 0000000..6004cef
--- /dev/null
+++ b/plugins/sysprof/gtk/menus.ui
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<interface>
+ <menu id="gear-menu">
+ <section id="gear-menu-open-section">
+ <item>
+ <attribute name="label" translatable="yes">Open Profile…</attribute>
+ <attribute name="action">profiler.open-profile</attribute>
+ </item>
+ </section>
+ </menu>
+</interface>
diff --git a/plugins/sysprof/sysprof.gresource.xml b/plugins/sysprof/sysprof.gresource.xml
new file mode 100644
index 0000000..2695fd8
--- /dev/null
+++ b/plugins/sysprof/sysprof.gresource.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/builder/plugins/sysprof-plugin">
+ <file>gtk/menus.ui</file>
+ <file>gbp-sysprof-perspective.ui</file>
+ </gresource>
+</gresources>
diff --git a/plugins/sysprof/sysprof.plugin b/plugins/sysprof/sysprof.plugin
new file mode 100644
index 0000000..784959d
--- /dev/null
+++ b/plugins/sysprof/sysprof.plugin
@@ -0,0 +1,7 @@
+[Plugin]
+Module=sysprof-plugin
+Name=Sysprof
+Description=Integration with the Sysprof system profiler
+Authors=Christian Hergert <christian hergert me>
+Copyright=Copyright © 2016 Christian Hergert
+Builtin=true
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]