2011-03-08 18:17:53 -05:00
|
|
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
|
|
|
|
/* gnome-shell-perf-helper: a program to create windows for performance tests
|
|
|
|
*
|
|
|
|
* Running performance tests with whatever windows a user has open results
|
|
|
|
* in unreliable results, so instead we hide all other windows and talk
|
|
|
|
* to this program over D-Bus to create just the windows we want.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2014-05-19 21:11:01 -04:00
|
|
|
#include <math.h>
|
|
|
|
|
2011-03-08 18:17:53 -05:00
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
|
|
|
#define BUS_NAME "org.gnome.Shell.PerfHelper"
|
|
|
|
|
|
|
|
static const gchar introspection_xml[] =
|
|
|
|
"<node>"
|
|
|
|
" <interface name='org.gnome.Shell.PerfHelper'>"
|
|
|
|
" <method name='Exit'/>"
|
|
|
|
" <method name='CreateWindow'>"
|
|
|
|
" <arg type='i' name='width' direction='in'/>"
|
|
|
|
" <arg type='i' name='height' direction='in'/>"
|
|
|
|
" <arg type='b' name='alpha' direction='in'/>"
|
|
|
|
" <arg type='b' name='maximized' direction='in'/>"
|
2014-05-19 21:11:01 -04:00
|
|
|
" <arg type='b' name='redraws' direction='in'/>"
|
2023-03-24 11:26:05 +01:00
|
|
|
" <arg type='b' name='text_input' direction='in'/>"
|
2011-03-08 18:17:53 -05:00
|
|
|
" </method>"
|
|
|
|
" <method name='WaitWindows'/>"
|
|
|
|
" <method name='DestroyWindows'/>"
|
|
|
|
" </interface>"
|
|
|
|
"</node>";
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
GtkWidget *window;
|
|
|
|
|
|
|
|
guint alpha : 1;
|
2014-05-19 21:11:01 -04:00
|
|
|
guint redraws : 1;
|
2011-03-08 18:17:53 -05:00
|
|
|
guint mapped : 1;
|
|
|
|
guint exposed : 1;
|
|
|
|
guint pending : 1;
|
2014-05-19 21:11:01 -04:00
|
|
|
|
|
|
|
gint64 start_time;
|
|
|
|
gint64 time;
|
2011-03-08 18:17:53 -05:00
|
|
|
} WindowInfo;
|
|
|
|
|
|
|
|
static int opt_idle_timeout = 30;
|
|
|
|
|
|
|
|
static GOptionEntry opt_entries[] =
|
|
|
|
{
|
|
|
|
{ "idle-timeout", 'r', 0, G_OPTION_ARG_INT, &opt_idle_timeout, "Exit after N seconds", "N" },
|
|
|
|
{ NULL }
|
|
|
|
};
|
|
|
|
|
2023-04-13 16:40:58 +02:00
|
|
|
#define PERF_HELPER_TYPE_APP (perf_helper_app_get_type ())
|
|
|
|
G_DECLARE_FINAL_TYPE (PerfHelperApp, perf_helper_app, PERF_HELPER, APP, GtkApplication)
|
|
|
|
|
|
|
|
struct _PerfHelperApp {
|
|
|
|
GtkApplication parent;
|
|
|
|
|
|
|
|
guint timeout_id;
|
|
|
|
GList *our_windows;
|
|
|
|
GList *wait_windows_invocations;
|
|
|
|
};
|
|
|
|
|
|
|
|
G_DEFINE_TYPE (PerfHelperApp, perf_helper_app, GTK_TYPE_APPLICATION);
|
|
|
|
|
|
|
|
static void destroy_windows (PerfHelperApp *app);
|
|
|
|
static void finish_wait_windows (PerfHelperApp *app);
|
|
|
|
static void check_finish_wait_windows (PerfHelperApp *app);
|
2011-03-08 18:17:53 -05:00
|
|
|
|
|
|
|
static gboolean
|
|
|
|
on_timeout (gpointer data)
|
|
|
|
{
|
2023-04-13 16:40:58 +02:00
|
|
|
PerfHelperApp *app = data;
|
|
|
|
app->timeout_id = 0;
|
2011-03-08 18:17:53 -05:00
|
|
|
|
2023-04-13 16:40:58 +02:00
|
|
|
destroy_windows (app);
|
|
|
|
g_application_quit (G_APPLICATION (app));
|
2011-03-08 18:17:53 -05:00
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2023-04-13 16:40:58 +02:00
|
|
|
establish_timeout (PerfHelperApp *app)
|
2011-03-08 18:17:53 -05:00
|
|
|
{
|
2023-04-13 16:40:58 +02:00
|
|
|
g_clear_handle_id (&app->timeout_id, g_source_remove);
|
2011-03-08 18:17:53 -05:00
|
|
|
|
2023-04-13 16:40:58 +02:00
|
|
|
app->timeout_id = g_timeout_add (opt_idle_timeout * 1000, on_timeout, app);
|
|
|
|
g_source_set_name_by_id (app->timeout_id, "[gnome-shell] on_timeout");
|
2011-03-08 18:17:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2023-04-13 16:40:58 +02:00
|
|
|
destroy_windows (PerfHelperApp *app)
|
2011-03-08 18:17:53 -05:00
|
|
|
{
|
|
|
|
GList *l;
|
|
|
|
|
2023-04-13 16:40:58 +02:00
|
|
|
for (l = app->our_windows; l; l = l->next)
|
2011-03-08 18:17:53 -05:00
|
|
|
{
|
|
|
|
WindowInfo *info = l->data;
|
|
|
|
gtk_widget_destroy (info->window);
|
|
|
|
g_free (info);
|
|
|
|
}
|
|
|
|
|
2023-04-13 16:40:58 +02:00
|
|
|
g_clear_list (&app->our_windows, NULL);
|
2011-03-08 18:17:53 -05:00
|
|
|
|
2023-04-13 16:40:58 +02:00
|
|
|
check_finish_wait_windows (app);
|
2011-03-08 18:17:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
on_window_map_event (GtkWidget *window,
|
|
|
|
GdkEventAny *event,
|
|
|
|
WindowInfo *info)
|
|
|
|
{
|
|
|
|
info->mapped = TRUE;
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2023-03-24 11:26:05 +01:00
|
|
|
static gboolean
|
|
|
|
on_window_draw (GtkWidget *window,
|
|
|
|
cairo_t *cr,
|
|
|
|
WindowInfo *info)
|
|
|
|
{
|
|
|
|
info->exposed = TRUE;
|
|
|
|
|
|
|
|
if (info->exposed && info->mapped && info->pending)
|
|
|
|
{
|
|
|
|
info->pending = FALSE;
|
2023-04-13 16:40:58 +02:00
|
|
|
check_finish_wait_windows (PERF_HELPER_APP (g_application_get_default ()));
|
2023-03-24 11:26:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2011-03-08 18:17:53 -05:00
|
|
|
static gboolean
|
2019-12-05 14:12:47 +01:00
|
|
|
on_child_draw (GtkWidget *window,
|
|
|
|
cairo_t *cr,
|
|
|
|
WindowInfo *info)
|
2011-03-08 18:17:53 -05:00
|
|
|
{
|
|
|
|
cairo_rectangle_int_t allocation;
|
2014-05-19 21:11:01 -04:00
|
|
|
double x_offset, y_offset;
|
2011-03-08 18:17:53 -05:00
|
|
|
|
2015-09-23 20:42:24 +02:00
|
|
|
gtk_widget_get_allocation (window, &allocation);
|
|
|
|
|
2011-03-08 18:17:53 -05:00
|
|
|
/* We draw an arbitrary pattern of red lines near the border of the
|
|
|
|
* window to make it more clear than empty windows if something
|
|
|
|
* is drastrically wrong.
|
|
|
|
*/
|
|
|
|
|
|
|
|
cairo_save (cr);
|
|
|
|
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
|
|
|
|
|
|
|
if (info->alpha)
|
|
|
|
cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
|
|
|
|
else
|
|
|
|
cairo_set_source_rgb (cr, 1, 1, 1);
|
|
|
|
|
|
|
|
cairo_paint (cr);
|
|
|
|
cairo_restore (cr);
|
|
|
|
|
2014-05-19 21:11:01 -04:00
|
|
|
if (info->redraws)
|
|
|
|
{
|
|
|
|
double position = (info->time - info->start_time) / 1000000.;
|
|
|
|
x_offset = 20 * cos (2 * M_PI * position);
|
|
|
|
y_offset = 20 * sin (2 * M_PI * position);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
x_offset = y_offset = 0;
|
|
|
|
}
|
|
|
|
|
2011-03-08 18:17:53 -05:00
|
|
|
cairo_set_source_rgb (cr, 1, 0, 0);
|
|
|
|
cairo_set_line_width (cr, 10);
|
2014-05-19 21:11:01 -04:00
|
|
|
cairo_move_to (cr, 0, 40 + y_offset);
|
|
|
|
cairo_line_to (cr, allocation.width, 40 + y_offset);
|
|
|
|
cairo_move_to (cr, 0, allocation.height - 40 + y_offset);
|
|
|
|
cairo_line_to (cr, allocation.width, allocation.height - 40 + y_offset);
|
|
|
|
cairo_move_to (cr, 40 + x_offset, 0);
|
|
|
|
cairo_line_to (cr, 40 + x_offset, allocation.height);
|
|
|
|
cairo_move_to (cr, allocation.width - 40 + x_offset, 0);
|
|
|
|
cairo_line_to (cr, allocation.width - 40 + x_offset, allocation.height);
|
2011-03-08 18:17:53 -05:00
|
|
|
cairo_stroke (cr);
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2014-05-19 21:11:01 -04:00
|
|
|
static gboolean
|
|
|
|
tick_callback (GtkWidget *widget,
|
|
|
|
GdkFrameClock *frame_clock,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
WindowInfo *info = user_data;
|
|
|
|
|
|
|
|
if (info->start_time < 0)
|
|
|
|
info->start_time = info->time = gdk_frame_clock_get_frame_time (frame_clock);
|
|
|
|
else
|
|
|
|
info->time = gdk_frame_clock_get_frame_time (frame_clock);
|
|
|
|
|
|
|
|
gtk_widget_queue_draw (widget);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2011-03-08 18:17:53 -05:00
|
|
|
static void
|
2023-04-13 16:40:58 +02:00
|
|
|
create_window (PerfHelperApp *app,
|
|
|
|
int width,
|
|
|
|
int height,
|
|
|
|
gboolean alpha,
|
|
|
|
gboolean maximized,
|
|
|
|
gboolean redraws,
|
|
|
|
gboolean text_input)
|
2011-03-08 18:17:53 -05:00
|
|
|
{
|
|
|
|
WindowInfo *info;
|
2019-12-05 14:12:47 +01:00
|
|
|
GtkWidget *child;
|
2011-03-08 18:17:53 -05:00
|
|
|
|
|
|
|
info = g_new0 (WindowInfo, 1);
|
|
|
|
info->alpha = alpha;
|
2014-05-19 21:11:01 -04:00
|
|
|
info->redraws = redraws;
|
2023-04-13 16:40:58 +02:00
|
|
|
info->window = gtk_application_window_new (GTK_APPLICATION (app));
|
2011-03-08 18:17:53 -05:00
|
|
|
if (alpha)
|
|
|
|
gtk_widget_set_visual (info->window, gdk_screen_get_rgba_visual (gdk_screen_get_default ()));
|
|
|
|
if (maximized)
|
|
|
|
gtk_window_maximize (GTK_WINDOW (info->window));
|
|
|
|
info->pending = TRUE;
|
2014-05-19 21:11:01 -04:00
|
|
|
info->start_time = -1;
|
2011-03-08 18:17:53 -05:00
|
|
|
|
2023-03-24 11:26:05 +01:00
|
|
|
if (text_input)
|
|
|
|
{
|
|
|
|
child = gtk_entry_new ();
|
|
|
|
gtk_widget_show (child);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
child = g_object_new (GTK_TYPE_BOX, "visible", TRUE, "app-paintable", TRUE, NULL);
|
|
|
|
gtk_widget_set_app_paintable (info->window, TRUE);
|
|
|
|
g_signal_connect (child, "draw", G_CALLBACK (on_child_draw), info);
|
|
|
|
}
|
|
|
|
|
2019-12-05 14:12:47 +01:00
|
|
|
gtk_container_add (GTK_CONTAINER (info->window), child);
|
|
|
|
|
2023-03-24 11:26:05 +01:00
|
|
|
g_signal_connect (info->window, "draw", G_CALLBACK (on_window_draw), info);
|
2011-03-08 18:17:53 -05:00
|
|
|
g_signal_connect (info->window, "map-event", G_CALLBACK (on_window_map_event), info);
|
2023-03-24 11:26:05 +01:00
|
|
|
|
|
|
|
gtk_widget_set_size_request (info->window, width, height);
|
2011-03-08 18:17:53 -05:00
|
|
|
gtk_widget_show (info->window);
|
|
|
|
|
2014-05-19 21:11:01 -04:00
|
|
|
if (info->redraws)
|
|
|
|
gtk_widget_add_tick_callback (info->window, tick_callback,
|
|
|
|
info, NULL);
|
|
|
|
|
2023-04-13 16:40:58 +02:00
|
|
|
app->our_windows = g_list_prepend (app->our_windows, info);
|
2011-03-08 18:17:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2023-04-13 16:40:58 +02:00
|
|
|
finish_wait_windows (PerfHelperApp *app)
|
2011-03-08 18:17:53 -05:00
|
|
|
{
|
|
|
|
GList *l;
|
|
|
|
|
2023-04-13 16:40:58 +02:00
|
|
|
for (l = app->wait_windows_invocations; l; l = l->next)
|
2011-03-08 18:17:53 -05:00
|
|
|
g_dbus_method_invocation_return_value (l->data, NULL);
|
|
|
|
|
2023-04-13 16:40:58 +02:00
|
|
|
g_clear_list (&app->wait_windows_invocations, NULL);
|
2011-03-08 18:17:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2023-04-13 16:40:58 +02:00
|
|
|
check_finish_wait_windows (PerfHelperApp *app)
|
2011-03-08 18:17:53 -05:00
|
|
|
{
|
|
|
|
GList *l;
|
|
|
|
gboolean have_pending = FALSE;
|
|
|
|
|
2023-04-13 16:40:58 +02:00
|
|
|
for (l = app->our_windows; l; l = l->next)
|
2011-03-08 18:17:53 -05:00
|
|
|
{
|
|
|
|
WindowInfo *info = l->data;
|
|
|
|
if (info->pending)
|
|
|
|
have_pending = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!have_pending)
|
2023-04-13 16:40:58 +02:00
|
|
|
finish_wait_windows (app);
|
2011-03-08 18:17:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
handle_method_call (GDBusConnection *connection,
|
|
|
|
const gchar *sender,
|
|
|
|
const gchar *object_path,
|
|
|
|
const gchar *interface_name,
|
|
|
|
const gchar *method_name,
|
|
|
|
GVariant *parameters,
|
|
|
|
GDBusMethodInvocation *invocation,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
2023-04-13 16:40:58 +02:00
|
|
|
PerfHelperApp *app = user_data;
|
|
|
|
|
2011-03-08 18:17:53 -05:00
|
|
|
/* Push off the idle timeout */
|
2023-04-13 16:40:58 +02:00
|
|
|
establish_timeout (app);
|
2011-03-08 18:17:53 -05:00
|
|
|
|
|
|
|
if (g_strcmp0 (method_name, "Exit") == 0)
|
|
|
|
{
|
2023-04-13 16:40:58 +02:00
|
|
|
destroy_windows (app);
|
2011-03-08 18:17:53 -05:00
|
|
|
|
|
|
|
g_dbus_method_invocation_return_value (invocation, NULL);
|
|
|
|
g_dbus_connection_flush_sync (connection, NULL, NULL);
|
|
|
|
|
2023-04-13 16:40:58 +02:00
|
|
|
g_application_quit (G_APPLICATION (app));
|
2011-03-08 18:17:53 -05:00
|
|
|
}
|
|
|
|
else if (g_strcmp0 (method_name, "CreateWindow") == 0)
|
|
|
|
{
|
|
|
|
int width, height;
|
2023-03-24 11:26:05 +01:00
|
|
|
gboolean alpha, maximized, redraws, text_input;
|
2011-03-08 18:17:53 -05:00
|
|
|
|
2023-03-24 11:26:05 +01:00
|
|
|
g_variant_get (parameters, "(iibbbb)",
|
|
|
|
&width, &height,
|
|
|
|
&alpha, &maximized, &redraws, &text_input);
|
2011-03-08 18:17:53 -05:00
|
|
|
|
2023-04-13 16:40:58 +02:00
|
|
|
create_window (app, width, height, alpha, maximized, redraws, text_input);
|
2011-03-08 18:17:53 -05:00
|
|
|
g_dbus_method_invocation_return_value (invocation, NULL);
|
|
|
|
}
|
|
|
|
else if (g_strcmp0 (method_name, "WaitWindows") == 0)
|
|
|
|
{
|
2023-04-13 16:40:58 +02:00
|
|
|
app->wait_windows_invocations = g_list_prepend (app->wait_windows_invocations, invocation);
|
|
|
|
check_finish_wait_windows (app);
|
2011-03-08 18:17:53 -05:00
|
|
|
}
|
|
|
|
else if (g_strcmp0 (method_name, "DestroyWindows") == 0)
|
|
|
|
{
|
2023-04-13 16:40:58 +02:00
|
|
|
destroy_windows (app);
|
2011-03-08 18:17:53 -05:00
|
|
|
g_dbus_method_invocation_return_value (invocation, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const GDBusInterfaceVTable interface_vtable =
|
|
|
|
{
|
|
|
|
handle_method_call,
|
|
|
|
NULL,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
2023-04-13 16:40:58 +02:00
|
|
|
perf_helper_app_activate (GApplication *app)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
perf_helper_app_dbus_register (GApplication *app,
|
|
|
|
GDBusConnection *connection,
|
|
|
|
const char *object_path,
|
|
|
|
GError **error)
|
2011-03-08 18:17:53 -05:00
|
|
|
{
|
|
|
|
GDBusNodeInfo *introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
|
|
|
|
|
|
|
|
g_dbus_connection_register_object (connection,
|
2023-04-13 16:40:58 +02:00
|
|
|
object_path,
|
2011-03-08 18:17:53 -05:00
|
|
|
introspection_data->interfaces[0],
|
|
|
|
&interface_vtable,
|
2023-04-13 16:40:58 +02:00
|
|
|
app, /* user_data */
|
2011-03-08 18:17:53 -05:00
|
|
|
NULL, /* user_data_free_func */
|
2023-04-13 16:40:58 +02:00
|
|
|
error);
|
|
|
|
return TRUE;
|
2011-03-08 18:17:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2023-04-13 16:40:58 +02:00
|
|
|
perf_helper_app_init (PerfHelperApp *app)
|
2011-03-08 18:17:53 -05:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2023-04-13 16:40:58 +02:00
|
|
|
perf_helper_app_class_init (PerfHelperAppClass *klass)
|
2011-03-08 18:17:53 -05:00
|
|
|
{
|
2023-04-13 16:40:58 +02:00
|
|
|
GApplicationClass *gapp_class = G_APPLICATION_CLASS (klass);
|
|
|
|
|
|
|
|
gapp_class->activate = perf_helper_app_activate;
|
|
|
|
gapp_class->dbus_register = perf_helper_app_dbus_register;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PerfHelperApp *
|
|
|
|
perf_helper_app_new (void) {
|
|
|
|
GApplicationFlags flags = G_APPLICATION_IS_SERVICE |
|
|
|
|
G_APPLICATION_ALLOW_REPLACEMENT |
|
|
|
|
G_APPLICATION_REPLACE;
|
|
|
|
|
|
|
|
return g_object_new (PERF_HELPER_TYPE_APP,
|
|
|
|
"application-id", "org.gnome.Shell.PerfHelper",
|
|
|
|
"flags", flags,
|
|
|
|
NULL);
|
2011-03-08 18:17:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main (int argc, char **argv)
|
|
|
|
{
|
2023-04-13 16:40:58 +02:00
|
|
|
PerfHelperApp *app = NULL;
|
2011-03-08 18:17:53 -05:00
|
|
|
|
|
|
|
/* Since we depend on this, avoid the possibility of lt-gnome-shell-perf-helper */
|
|
|
|
g_set_prgname ("gnome-shell-perf-helper");
|
|
|
|
|
2023-04-13 16:40:58 +02:00
|
|
|
app = perf_helper_app_new ();
|
2011-03-08 18:17:53 -05:00
|
|
|
|
2023-04-13 16:40:58 +02:00
|
|
|
g_application_set_option_context_summary (G_APPLICATION (app),
|
|
|
|
"Server to create windows for performance testing");
|
|
|
|
g_application_add_main_option_entries (G_APPLICATION (app), opt_entries);
|
2011-03-08 18:17:53 -05:00
|
|
|
|
2023-04-13 16:40:58 +02:00
|
|
|
g_application_hold (G_APPLICATION (app));
|
|
|
|
establish_timeout (app);
|
2011-03-08 18:17:53 -05:00
|
|
|
|
2023-04-13 16:40:58 +02:00
|
|
|
return g_application_run (G_APPLICATION (app), argc, argv);
|
2011-03-08 18:17:53 -05:00
|
|
|
}
|