Blame src/shell-perf-helper.c

Packit Service ed5168
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
Packit Service ed5168
Packit Service ed5168
/* gnome-shell-perf-helper: a program to create windows for performance tests
Packit Service ed5168
 *
Packit Service ed5168
 * Running performance tests with whatever windows a user has open results
Packit Service ed5168
 * in unreliable results, so instead we hide all other windows and talk
Packit Service ed5168
 * to this program over D-Bus to create just the windows we want.
Packit Service ed5168
 */
Packit Service ed5168
Packit Service ed5168
#include "config.h"
Packit Service ed5168
Packit Service ed5168
#include <math.h>
Packit Service ed5168
Packit Service ed5168
#include <gtk/gtk.h>
Packit Service eca014
#include <gdk/gdkx.h>
Packit Service ed5168
Packit Service ed5168
#define BUS_NAME "org.gnome.Shell.PerfHelper"
Packit Service ed5168
Packit Service ed5168
static void destroy_windows           (void);
Packit Service ed5168
static void finish_wait_windows       (void);
Packit Service ed5168
static void check_finish_wait_windows (void);
Packit Service ed5168
Packit Service ed5168
static const gchar introspection_xml[] =
Packit Service ed5168
	  "<node>"
Packit Service ed5168
	  "  <interface name='org.gnome.Shell.PerfHelper'>"
Packit Service ed5168
	  "    <method name='Exit'/>"
Packit Service ed5168
	  "    <method name='CreateWindow'>"
Packit Service ed5168
	  "      <arg type='i' name='width' direction='in'/>"
Packit Service ed5168
	  "      <arg type='i' name='height' direction='in'/>"
Packit Service ed5168
	  "      <arg type='b' name='alpha' direction='in'/>"
Packit Service ed5168
	  "      <arg type='b' name='maximized' direction='in'/>"
Packit Service ed5168
	  "      <arg type='b' name='redraws' direction='in'/>"
Packit Service ed5168
	  "    </method>"
Packit Service ed5168
	  "    <method name='WaitWindows'/>"
Packit Service ed5168
	  "    <method name='DestroyWindows'/>"
Packit Service ed5168
	  "  </interface>"
Packit Service ed5168
	"</node>";
Packit Service ed5168
Packit Service ed5168
typedef struct {
Packit Service ed5168
  GtkWidget *window;
Packit Service ed5168
  int width;
Packit Service ed5168
  int height;
Packit Service ed5168
Packit Service ed5168
  guint alpha : 1;
Packit Service ed5168
  guint maximized : 1;
Packit Service ed5168
  guint redraws : 1;
Packit Service ed5168
  guint mapped : 1;
Packit Service ed5168
  guint exposed : 1;
Packit Service ed5168
  guint pending : 1;
Packit Service ed5168
Packit Service ed5168
  gint64 start_time;
Packit Service ed5168
  gint64 time;
Packit Service ed5168
} WindowInfo;
Packit Service ed5168
Packit Service ed5168
static int opt_idle_timeout = 30;
Packit Service ed5168
Packit Service ed5168
static GOptionEntry opt_entries[] =
Packit Service ed5168
  {
Packit Service ed5168
    { "idle-timeout", 'r', 0, G_OPTION_ARG_INT, &opt_idle_timeout, "Exit after N seconds", "N" },
Packit Service ed5168
    { NULL }
Packit Service ed5168
  };
Packit Service ed5168
Packit Service eca014
static Display *xdisplay;
Packit Service eca014
static Window xroot;
Packit Service eca014
static Atom atom_wm_state;
Packit Service eca014
static Atom atom__net_wm_name;
Packit Service eca014
static Atom atom_utf8_string;
Packit Service eca014
Packit Service ed5168
static guint timeout_id;
Packit Service ed5168
static GList *our_windows;
Packit Service ed5168
static GList *wait_windows_invocations;
Packit Service ed5168
Packit Service ed5168
static gboolean
Packit Service ed5168
on_timeout (gpointer data)
Packit Service ed5168
{
Packit Service ed5168
  timeout_id = 0;
Packit Service ed5168
Packit Service ed5168
  destroy_windows ();
Packit Service ed5168
  gtk_main_quit ();
Packit Service ed5168
Packit Service ed5168
  return FALSE;
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
establish_timeout (void)
Packit Service ed5168
{
Packit Service ed5168
  if (timeout_id != 0)
Packit Service ed5168
    g_source_remove (timeout_id);
Packit Service ed5168
Packit Service ed5168
  timeout_id = g_timeout_add (opt_idle_timeout * 1000, on_timeout, NULL);
Packit Service ed5168
  g_source_set_name_by_id (timeout_id, "[gnome-shell] on_timeout");
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
destroy_windows (void)
Packit Service ed5168
{
Packit Service ed5168
  GList *l;
Packit Service ed5168
Packit Service ed5168
  for (l = our_windows; l; l = l->next)
Packit Service ed5168
    {
Packit Service ed5168
      WindowInfo *info = l->data;
Packit Service ed5168
      gtk_widget_destroy (info->window);
Packit Service ed5168
      g_free (info);
Packit Service ed5168
    }
Packit Service ed5168
Packit Service ed5168
  g_list_free (our_windows);
Packit Service ed5168
  our_windows = NULL;
Packit Service ed5168
Packit Service ed5168
  check_finish_wait_windows ();
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static gboolean
Packit Service ed5168
on_window_map_event (GtkWidget   *window,
Packit Service ed5168
                     GdkEventAny *event,
Packit Service ed5168
                     WindowInfo  *info)
Packit Service ed5168
{
Packit Service ed5168
  info->mapped = TRUE;
Packit Service ed5168
Packit Service ed5168
  return FALSE;
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static gboolean
Packit Service eca014
on_window_draw (GtkWidget  *window,
Packit Service eca014
		cairo_t    *cr,
Packit Service eca014
                WindowInfo *info)
Packit Service ed5168
{
Packit Service ed5168
  cairo_rectangle_int_t allocation;
Packit Service ed5168
  double x_offset, y_offset;
Packit Service ed5168
Packit Service ed5168
  gtk_widget_get_allocation (window, &allocation);
Packit Service ed5168
Packit Service ed5168
  /* We draw an arbitrary pattern of red lines near the border of the
Packit Service ed5168
   * window to make it more clear than empty windows if something
Packit Service ed5168
   * is drastrically wrong.
Packit Service ed5168
   */
Packit Service ed5168
Packit Service ed5168
  cairo_save (cr);
Packit Service ed5168
  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
Packit Service ed5168
Packit Service ed5168
  if (info->alpha)
Packit Service ed5168
    cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
Packit Service ed5168
  else
Packit Service ed5168
    cairo_set_source_rgb (cr, 1, 1, 1);
Packit Service ed5168
Packit Service ed5168
  cairo_paint (cr);
Packit Service ed5168
  cairo_restore (cr);
Packit Service ed5168
Packit Service ed5168
  if (info->redraws)
Packit Service ed5168
    {
Packit Service ed5168
      double position = (info->time - info->start_time) / 1000000.;
Packit Service ed5168
      x_offset = 20 * cos (2 * M_PI * position);
Packit Service ed5168
      y_offset = 20 * sin (2 * M_PI * position);
Packit Service ed5168
    }
Packit Service ed5168
  else
Packit Service ed5168
    {
Packit Service ed5168
      x_offset = y_offset = 0;
Packit Service ed5168
    }
Packit Service ed5168
Packit Service ed5168
  cairo_set_source_rgb (cr, 1, 0, 0);
Packit Service ed5168
  cairo_set_line_width (cr, 10);
Packit Service ed5168
  cairo_move_to (cr, 0, 40 + y_offset);
Packit Service ed5168
  cairo_line_to (cr, allocation.width, 40 + y_offset);
Packit Service ed5168
  cairo_move_to (cr, 0, allocation.height - 40 + y_offset);
Packit Service ed5168
  cairo_line_to (cr, allocation.width, allocation.height - 40 + y_offset);
Packit Service ed5168
  cairo_move_to (cr, 40 + x_offset, 0);
Packit Service ed5168
  cairo_line_to (cr, 40 + x_offset, allocation.height);
Packit Service ed5168
  cairo_move_to (cr, allocation.width - 40 + x_offset, 0);
Packit Service ed5168
  cairo_line_to (cr, allocation.width - 40 + x_offset, allocation.height);
Packit Service ed5168
  cairo_stroke (cr);
Packit Service ed5168
Packit Service ed5168
  info->exposed = TRUE;
Packit Service ed5168
Packit Service ed5168
  if (info->exposed && info->mapped && info->pending)
Packit Service ed5168
    {
Packit Service ed5168
      info->pending = FALSE;
Packit Service ed5168
      check_finish_wait_windows ();
Packit Service ed5168
    }
Packit Service ed5168
Packit Service ed5168
  return FALSE;
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static gboolean
Packit Service ed5168
tick_callback (GtkWidget     *widget,
Packit Service ed5168
               GdkFrameClock *frame_clock,
Packit Service ed5168
               gpointer       user_data)
Packit Service ed5168
{
Packit Service ed5168
  WindowInfo *info = user_data;
Packit Service ed5168
Packit Service ed5168
  if (info->start_time < 0)
Packit Service ed5168
    info->start_time = info->time = gdk_frame_clock_get_frame_time (frame_clock);
Packit Service ed5168
  else
Packit Service ed5168
    info->time = gdk_frame_clock_get_frame_time (frame_clock);
Packit Service ed5168
Packit Service ed5168
  gtk_widget_queue_draw (widget);
Packit Service ed5168
Packit Service ed5168
  return TRUE;
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
create_window (int      width,
Packit Service ed5168
	       int      height,
Packit Service ed5168
               gboolean alpha,
Packit Service ed5168
               gboolean maximized,
Packit Service ed5168
               gboolean redraws)
Packit Service ed5168
{
Packit Service ed5168
  WindowInfo *info;
Packit Service ed5168
Packit Service ed5168
  info = g_new0 (WindowInfo, 1);
Packit Service ed5168
  info->width = width;
Packit Service ed5168
  info->height = height;
Packit Service ed5168
  info->alpha = alpha;
Packit Service ed5168
  info->maximized = maximized;
Packit Service ed5168
  info->redraws = redraws;
Packit Service ed5168
  info->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
Packit Service ed5168
  if (alpha)
Packit Service ed5168
    gtk_widget_set_visual (info->window, gdk_screen_get_rgba_visual (gdk_screen_get_default ()));
Packit Service ed5168
  if (maximized)
Packit Service ed5168
    gtk_window_maximize (GTK_WINDOW (info->window));
Packit Service ed5168
  info->pending = TRUE;
Packit Service ed5168
  info->start_time = -1;
Packit Service ed5168
Packit Service ed5168
  gtk_widget_set_size_request (info->window, width, height);
Packit Service ed5168
  gtk_widget_set_app_paintable (info->window, TRUE);
Packit Service ed5168
  g_signal_connect (info->window, "map-event", G_CALLBACK (on_window_map_event), info);
Packit Service eca014
  g_signal_connect (info->window, "draw", G_CALLBACK (on_window_draw), info);
Packit Service ed5168
  gtk_widget_show (info->window);
Packit Service ed5168
Packit Service ed5168
  if (info->redraws)
Packit Service ed5168
    gtk_widget_add_tick_callback (info->window, tick_callback,
Packit Service ed5168
                                  info, NULL);
Packit Service ed5168
Packit Service ed5168
  our_windows = g_list_prepend (our_windows, info);
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
finish_wait_windows (void)
Packit Service ed5168
{
Packit Service ed5168
  GList *l;
Packit Service ed5168
Packit Service ed5168
  for (l = wait_windows_invocations; l; l = l->next)
Packit Service ed5168
    g_dbus_method_invocation_return_value (l->data, NULL);
Packit Service ed5168
Packit Service ed5168
  g_list_free (wait_windows_invocations);
Packit Service ed5168
  wait_windows_invocations = NULL;
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
check_finish_wait_windows (void)
Packit Service ed5168
{
Packit Service ed5168
  GList *l;
Packit Service ed5168
  gboolean have_pending = FALSE;
Packit Service ed5168
Packit Service ed5168
  for (l = our_windows; l; l = l->next)
Packit Service ed5168
    {
Packit Service ed5168
      WindowInfo *info = l->data;
Packit Service ed5168
      if (info->pending)
Packit Service ed5168
        have_pending = TRUE;
Packit Service ed5168
    }
Packit Service ed5168
Packit Service ed5168
  if (!have_pending)
Packit Service ed5168
    finish_wait_windows ();
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
handle_method_call (GDBusConnection       *connection,
Packit Service ed5168
		    const gchar           *sender,
Packit Service ed5168
		    const gchar           *object_path,
Packit Service ed5168
		    const gchar           *interface_name,
Packit Service ed5168
		    const gchar           *method_name,
Packit Service ed5168
		    GVariant              *parameters,
Packit Service ed5168
		    GDBusMethodInvocation *invocation,
Packit Service ed5168
		    gpointer               user_data)
Packit Service ed5168
{
Packit Service ed5168
  /* Push off the idle timeout */
Packit Service ed5168
  establish_timeout ();
Packit Service ed5168
Packit Service ed5168
  if (g_strcmp0 (method_name, "Exit") == 0)
Packit Service ed5168
    {
Packit Service ed5168
      destroy_windows ();
Packit Service ed5168
Packit Service ed5168
      g_dbus_method_invocation_return_value (invocation, NULL);
Packit Service ed5168
      g_dbus_connection_flush_sync (connection, NULL, NULL);
Packit Service ed5168
Packit Service ed5168
      gtk_main_quit ();
Packit Service ed5168
    }
Packit Service ed5168
  else if (g_strcmp0 (method_name, "CreateWindow") == 0)
Packit Service ed5168
    {
Packit Service ed5168
      int width, height;
Packit Service ed5168
      gboolean alpha, maximized, redraws;
Packit Service ed5168
Packit Service ed5168
      g_variant_get (parameters, "(iibbb)", &width, &height, &alpha, &maximized, &redraws);
Packit Service ed5168
Packit Service ed5168
      create_window (width, height, alpha, maximized, redraws);
Packit Service ed5168
      g_dbus_method_invocation_return_value (invocation, NULL);
Packit Service ed5168
    }
Packit Service ed5168
  else if (g_strcmp0 (method_name, "WaitWindows") == 0)
Packit Service ed5168
    {
Packit Service ed5168
      wait_windows_invocations = g_list_prepend (wait_windows_invocations, invocation);
Packit Service ed5168
      check_finish_wait_windows ();
Packit Service ed5168
    }
Packit Service ed5168
  else if (g_strcmp0 (method_name, "DestroyWindows") == 0)
Packit Service ed5168
    {
Packit Service ed5168
      destroy_windows ();
Packit Service ed5168
      g_dbus_method_invocation_return_value (invocation, NULL);
Packit Service ed5168
    }
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static const GDBusInterfaceVTable interface_vtable =
Packit Service ed5168
{
Packit Service ed5168
  handle_method_call,
Packit Service ed5168
  NULL,
Packit Service ed5168
  NULL
Packit Service ed5168
};
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
on_bus_acquired (GDBusConnection *connection,
Packit Service ed5168
		 const gchar     *name,
Packit Service ed5168
		 gpointer         user_data)
Packit Service ed5168
{
Packit Service ed5168
  GDBusNodeInfo *introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
Packit Service ed5168
Packit Service ed5168
  g_dbus_connection_register_object (connection,
Packit Service ed5168
				     "/org/gnome/Shell/PerfHelper",
Packit Service ed5168
				     introspection_data->interfaces[0],
Packit Service ed5168
				     &interface_vtable,
Packit Service ed5168
				     NULL,  /* user_data */
Packit Service ed5168
				     NULL,  /* user_data_free_func */
Packit Service ed5168
				     NULL); /* GError** */
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
on_name_acquired (GDBusConnection *connection,
Packit Service ed5168
		  const gchar     *name,
Packit Service ed5168
		  gpointer         user_data)
Packit Service ed5168
{
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
on_name_lost  (GDBusConnection *connection,
Packit Service ed5168
	       const gchar     *name,
Packit Service ed5168
	       gpointer         user_data)
Packit Service ed5168
{
Packit Service ed5168
  destroy_windows ();
Packit Service ed5168
  gtk_main_quit ();
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
int
Packit Service ed5168
main (int argc, char **argv)
Packit Service ed5168
{
Packit Service eca014
  GdkDisplay *display;
Packit Service eca014
  GdkScreen *screen;
Packit Service ed5168
  GOptionContext *context;
Packit Service ed5168
  GError *error = NULL;
Packit Service ed5168
Packit Service ed5168
  /* Since we depend on this, avoid the possibility of lt-gnome-shell-perf-helper */
Packit Service ed5168
  g_set_prgname ("gnome-shell-perf-helper");
Packit Service ed5168
Packit Service ed5168
  context = g_option_context_new (" - server to create windows for performance testing");
Packit Service ed5168
  g_option_context_add_main_entries (context, opt_entries, NULL);
Packit Service ed5168
  g_option_context_add_group (context, gtk_get_option_group (TRUE));
Packit Service ed5168
  if (!g_option_context_parse (context, &argc, &argv, &error))
Packit Service ed5168
    {
Packit Service ed5168
      g_print ("option parsing failed: %s\n", error->message);
Packit Service ed5168
      return 1;
Packit Service ed5168
    }
Packit Service ed5168
Packit Service eca014
  display = gdk_display_get_default ();
Packit Service eca014
  screen = gdk_screen_get_default ();
Packit Service eca014
Packit Service eca014
  xdisplay = gdk_x11_display_get_xdisplay (display);
Packit Service eca014
  xroot = gdk_x11_window_get_xid (gdk_screen_get_root_window (screen));
Packit Service eca014
  atom_wm_state = gdk_x11_get_xatom_by_name_for_display (display, "WM_STATE");
Packit Service eca014
  atom__net_wm_name = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_NAME");
Packit Service eca014
  atom_utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING");
Packit Service eca014
Packit Service ed5168
  g_bus_own_name (G_BUS_TYPE_SESSION,
Packit Service ed5168
                  BUS_NAME,
Packit Service ed5168
                  G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
Packit Service ed5168
                  G_BUS_NAME_OWNER_FLAGS_REPLACE,
Packit Service ed5168
                  on_bus_acquired,
Packit Service ed5168
                  on_name_acquired,
Packit Service ed5168
                  on_name_lost,
Packit Service ed5168
                  NULL,
Packit Service ed5168
                  NULL);
Packit Service ed5168
Packit Service ed5168
  establish_timeout ();
Packit Service ed5168
Packit Service ed5168
  gtk_main ();
Packit Service ed5168
Packit Service ed5168
  return 0;
Packit Service ed5168
}