static Bool
try_get_reply (Display           *xdisplay,
               AgGetPropertyTask *task)
{
  if (ag_task_have_reply (task))
    {
      int result;
      Atom actual_type;
      int actual_format;
      unsigned long n_items;
      unsigned long bytes_after;
      unsigned char *data;
      char *name;
      struct timeval current_time;

      gettimeofday (&current_time, NULL);
      
      printf (" %gms (we have a reply for property %ld)\n",
              ELAPSED (program_start_time, current_time),
              ag_task_get_property (task));
      
      data = NULL;

      name = atom_name (xdisplay,
                        ag_task_get_property (task));
      printf (" %s on 0x%lx:\n", name,
              ag_task_get_window (task));
      free (name);
      
      result = ag_task_get_reply_and_free (task,
                                           &actual_type,
                                           &actual_format,
                                           &n_items,
                                           &bytes_after,
                                           &data);
      task = NULL;

      if (result != Success)
        {
          fprintf (stderr, "  error code %d getting reply\n", result);
        }
      else
        {
          name = atom_name (xdisplay, actual_type);
          printf ("  actual_type = %s\n", name);
          free (name);
          
          printf ("  actual_format = %d\n", actual_format);
          
          printf ("  n_items = %lu\n", n_items);
          printf ("  bytes_after = %lu\n", bytes_after);
          
          printf ("  data = \"%s\"\n", data ? (char*) data : "NULL");
        }

      return True;
    }

  return False;
}
void
meta_prop_get_values (MetaDisplay   *display,
                      Window         xwindow,
                      MetaPropValue *values,
                      int            n_values)
{
  int i;
  AgGetPropertyTask **tasks;

  meta_verbose ("Requesting %d properties of 0x%lx at once\n",
                n_values, xwindow);
  
  if (n_values == 0)
    return;
  
  tasks = g_new0 (AgGetPropertyTask*, n_values);

  /* Start up tasks. The "values" array can have values
   * with atom == None, which means to ignore that element.
   */
  i = 0;
  while (i < n_values)
    {
      if (values[i].required_type == None)
        {
          switch (values[i].type)
            {
            case META_PROP_VALUE_INVALID:
              /* This means we don't really want a value, e.g. got
               * property notify on an atom we don't care about.
               */
              if (values[i].atom != None)
                meta_bug ("META_PROP_VALUE_INVALID requested in %s\n", G_STRFUNC);
              break;
            case META_PROP_VALUE_UTF8_LIST:
            case META_PROP_VALUE_UTF8:
              values[i].required_type = display->atom_UTF8_STRING;
              break;
            case META_PROP_VALUE_STRING:
            case META_PROP_VALUE_STRING_AS_UTF8:
              values[i].required_type = XA_STRING;
              break;
            case META_PROP_VALUE_MOTIF_HINTS:
              values[i].required_type = AnyPropertyType;
              break;
            case META_PROP_VALUE_CARDINAL_LIST:
            case META_PROP_VALUE_CARDINAL:
              values[i].required_type = XA_CARDINAL;
              break;
            case META_PROP_VALUE_WINDOW:
              values[i].required_type = XA_WINDOW;
              break;
            case META_PROP_VALUE_ATOM_LIST:
              values[i].required_type = XA_ATOM;
              break;
            case META_PROP_VALUE_TEXT_PROPERTY:
              values[i].required_type = AnyPropertyType;
              break;
            case META_PROP_VALUE_WM_HINTS:
              values[i].required_type = XA_WM_HINTS;
              break;
            case META_PROP_VALUE_CLASS_HINT:
              values[i].required_type = XA_STRING;
              break;
            case META_PROP_VALUE_SIZE_HINTS:
              values[i].required_type = XA_WM_SIZE_HINTS;
              break;
            case META_PROP_VALUE_SYNC_COUNTER:
	      values[i].required_type = XA_CARDINAL;
              break;
            }
        }

      if (values[i].atom != None)
        tasks[i] = get_task (display, xwindow,
                             values[i].atom, values[i].required_type);
      
      ++i;
    }  
  
  /* Get replies for all our tasks */
  meta_topic (META_DEBUG_SYNC, "Syncing to get %d GetProperty replies in %s\n",
              n_values, G_STRFUNC);
  XSync (display->xdisplay, False);
  
  /* Collect results, should arrive in order requested */
  i = 0;
  while (i < n_values)
    {
      AgGetPropertyTask *task;
      GetPropertyResults results;
      
      if (tasks[i] == NULL)
        {
          /* Probably values[i].type was None, or ag_task_create()
           * returned NULL.
           */
          values[i].type = META_PROP_VALUE_INVALID;
          goto next;
        }
      
      task = ag_get_next_completed_task (display->xdisplay);
      g_assert (task != NULL);
      g_assert (ag_task_have_reply (task));

      results.display = display;
      results.xwindow = xwindow;
      results.xatom = values[i].atom;
      results.prop = NULL;
      results.n_items = 0;
      results.type = None;
      results.bytes_after = 0;
      results.format = 0;
      
      if (ag_task_get_reply_and_free (task,
                                      &results.type, &results.format,
                                      &results.n_items,
                                      &results.bytes_after,
                                      &results.prop) != Success ||
          results.type == None)
        {
          values[i].type = META_PROP_VALUE_INVALID;
          if (results.prop)
            {
              XFree (results.prop);
              results.prop = NULL;
            }
          goto next;
        }

      switch (values[i].type)
        {
        case META_PROP_VALUE_INVALID:
          g_assert_not_reached ();
          break;
        case META_PROP_VALUE_UTF8_LIST:
          if (!utf8_list_from_results (&results,
                                       &values[i].v.string_list.strings,
                                       &values[i].v.string_list.n_strings))
            values[i].type = META_PROP_VALUE_INVALID;
          break;
        case META_PROP_VALUE_UTF8:
          if (!utf8_string_from_results (&results,
                                         &values[i].v.str))
            values[i].type = META_PROP_VALUE_INVALID;
          break;
        case META_PROP_VALUE_STRING:
          if (!latin1_string_from_results (&results,
                                           &values[i].v.str))
            values[i].type = META_PROP_VALUE_INVALID;
          break;
        case META_PROP_VALUE_STRING_AS_UTF8:
          if (!latin1_string_from_results (&results,
                                           &values[i].v.str))
            values[i].type = META_PROP_VALUE_INVALID;
          else
            {
              char *new_str;
              char *xmalloc_new_str;

              new_str = latin1_to_utf8 (values[i].v.str);
              xmalloc_new_str = ag_Xmalloc (strlen (new_str) + 1);
              if (xmalloc_new_str != NULL)
                {
                  strcpy (xmalloc_new_str, new_str);
                  meta_XFree (values[i].v.str);
                  values[i].v.str = xmalloc_new_str;
                }

              g_free (new_str);
            }
          break;
        case META_PROP_VALUE_MOTIF_HINTS:
          if (!motif_hints_from_results (&results,
                                         &values[i].v.motif_hints))
            values[i].type = META_PROP_VALUE_INVALID;
          break;
        case META_PROP_VALUE_CARDINAL_LIST:
          if (!cardinal_list_from_results (&results,
                                           &values[i].v.cardinal_list.cardinals,
                                           &values[i].v.cardinal_list.n_cardinals))
            values[i].type = META_PROP_VALUE_INVALID;
          break;
        case META_PROP_VALUE_CARDINAL:
          if (!cardinal_with_atom_type_from_results (&results,
                                                     values[i].required_type,
                                                     &values[i].v.cardinal))
            values[i].type = META_PROP_VALUE_INVALID;
          break;
        case META_PROP_VALUE_WINDOW:
          if (!window_from_results (&results,
                                    &values[i].v.xwindow))
            values[i].type = META_PROP_VALUE_INVALID;
          break;
        case META_PROP_VALUE_ATOM_LIST:
          if (!atom_list_from_results (&results,
                                       &values[i].v.atom_list.atoms,
                                       &values[i].v.atom_list.n_atoms))
            values[i].type = META_PROP_VALUE_INVALID;
          break;
        case META_PROP_VALUE_TEXT_PROPERTY:
          if (!text_property_from_results (&results, &values[i].v.str))
            values[i].type = META_PROP_VALUE_INVALID;
          break;
        case META_PROP_VALUE_WM_HINTS:
          if (!wm_hints_from_results (&results, &values[i].v.wm_hints))
            values[i].type = META_PROP_VALUE_INVALID;
          break;
        case META_PROP_VALUE_CLASS_HINT:
          if (!class_hint_from_results (&results, &values[i].v.class_hint))
            values[i].type = META_PROP_VALUE_INVALID;
          break;
        case META_PROP_VALUE_SIZE_HINTS:
          if (!size_hints_from_results (&results,
                                        &values[i].v.size_hints.hints,
                                        &values[i].v.size_hints.flags))
            values[i].type = META_PROP_VALUE_INVALID;
          break;
        case META_PROP_VALUE_SYNC_COUNTER:
#ifdef HAVE_XSYNC
          if (!counter_from_results (&results,
                                     &values[i].v.xcounter))
            values[i].type = META_PROP_VALUE_INVALID;
#else
          values[i].type = META_PROP_VALUE_INVALID;
          if (results.prop)
            {
              XFree (results.prop);
              results.prop = NULL;
            }
#endif
          break;
        }

    next:
      ++i;
    }

  g_free (tasks);
}
/* This function doesn't have all the printf's
 * and other noise, it just compares async to sync
 */
static void
run_speed_comparison (Display *xdisplay,
                      Window   window)
{
  int i;
  int n_props;
  struct timeval start, end;
  int n_left;
  
  /* We just use atom values (0 to n_props) % 200, many are probably
   * BadAtom, that's fine, but the %200 keeps most of them valid. The
   * async case is about twice as advantageous when using valid atoms
   * (or the issue may be that it's more advantageous when the
   * properties are present and data is transmitted).
   */
  n_props = 4000;
  printf ("Timing with %d property requests\n", n_props);
  
  gettimeofday (&start, NULL);
  
  i = 0;
  while (i < n_props)
    {
      if (ag_task_create (xdisplay,
                          window, (Atom) i % 200,
                          0, 0xffffffff,
                          False,
                          AnyPropertyType) == NULL)
        {
          fprintf (stderr, "Failed to send request\n");
          exit (1);
        }
      
      ++i;
    }

  n_left = n_props;
  
  while (TRUE)
    {
      int connection;
      fd_set set;
      XEvent xevent;
      AgGetPropertyTask *task;
      
      /* Mop up event queue */
      while (XPending (xdisplay) > 0)
        XNextEvent (xdisplay, &xevent);
      
      while ((task = ag_get_next_completed_task (xdisplay)))
        {
          int UNUSED_VARIABLE result;
          Atom actual_type;
          int actual_format;
          unsigned long n_items;
          unsigned long bytes_after;
          unsigned char *data;

          assert (ag_task_have_reply (task));
          
          data = NULL;
          result = ag_task_get_reply_and_free (task,
                                               &actual_type,
                                               &actual_format,
                                               &n_items,
                                               &bytes_after,
                                               &data);
          
          if (data)
            XFree (data);
          
          n_left -= 1;
        }
      
      if (n_left == 0)
        break;

      /* Wake up if we may have a reply */
      connection = ConnectionNumber (xdisplay);

      FD_ZERO (&set);
      FD_SET (connection, &set);

      select (connection + 1, &set, NULL, NULL, NULL);
    }
  
  gettimeofday (&end, NULL);
  
  printf ("Async time: %gms\n",
          ELAPSED (start, end));
  
  gettimeofday (&start, NULL);

  error_trap_push (xdisplay);
  
  i = 0;
  while (i < n_props)
    {
      Atom actual_type;
      int actual_format;
      unsigned long n_items;
      unsigned long bytes_after;
      unsigned char *data;
      
      data = NULL;
      if (XGetWindowProperty (xdisplay, window,
                              (Atom) i % 200,
                              0, 0xffffffff,
                              False,
                              AnyPropertyType,
                              &actual_type,
                              &actual_format,
                              &n_items,
                              &bytes_after,
                              &data) == Success)
        {
          if (data)
            XFree (data);
        }
      
      ++i;
    }

  error_trap_pop (xdisplay);
  
  gettimeofday (&end, NULL);
  
  printf ("Sync time:  %gms\n",
          ELAPSED (start, end));
}