/* 

_NET_WM_SYNC_REQUEST

This protocol uses the XSync extension the protocol specification and
the library documentation<) to let client and window manager
synchronize the repaint of the window manager frame and the client
window. A client indicates that it is willing to participate in the
protocol by listing _NET_WM_SYNC_REQUEST in the WM_PROTOCOLS property
of the client window and storing the XID of an XSync counter in the
property _NET_WM_SYNC_REQUEST_COUNTER. The initial value of this
counter is not defined by this specification.

A window manager uses this protocol by preceding a ConfigureNotify
event sent to a client by a client message as follows:

type = ClientMessage
window = the respective client window
message_type = WM_PROTOCOLS
format = 32
data.l[0] = _NET_WM_SYNC_REQUEST
data.l[1] = timestamp
data.l[2] = low 32 bits of the update request number
data.l[3] = high 32 bits of the update request number
other data.l[] elements = 0

After receiving one or more such message/ConfigureNotify pairs, and
having handled all repainting associated with the ConfigureNotify
events, the client MUST set the _NET_WM_SYNC_REQUEST_COUNTER to the 64
bit number indicated by the data.l[2] and data.l[3] fields of the last
client message received.

By using either the Alarm or the Await mechanisms of the XSync
extension, the window manager can know when the client has finished
handling the ConfigureNotify events. The window manager SHOULD not
resize the window faster than the client can keep up.

The update request number in the client message is determined by the
window manager subject to the restriction that it MUST NOT be 0. The
number is generally intended to be incremented by one for each message
sent. Since the initial value of the XSync counter is not defined by
this specification, the window manager MAY set the value of the XSync
counter at any time, and MUST do so when it first manages a new
window.

*/

#ifdef USE_XSYNC

static void sync_value_increment (XSyncValue *value)
{
  XSyncValue one;
  int overflow;
  
  XSyncIntToValue (&one, 1);
  XSyncValueAdd (value, *value, one, &overflow);
}

void sync_init(void)
{
  if (!XSyncQueryExtension (dpy,
                            sync_event_base,
                            sync_error_base))
    {
      dbg("%s() XSyncQueryExtension FAILED.\n", __func__);
      have_xsync = False;
      return;
    }

  have_xsync = True;
}

void sync_handle_event(XSyncAlarmNotifyEvent *ev)
{
  Client *client = NULL;

  //stack_enumerate(w, client)
    
  if (client->sync_alarm == ev->alarm)
	break;

  if (client) 			/* XXX should check alarm matches */
    {
      dbg("%s() found client %s\n", __func__, client->name);
      client->move_resize(client);
    }
}

Bool sync_client_move_resize(Client *client)
{
  Wm *w = client->wm;
  unsigned long highval, lowval;

  if (!have_xsync) 
    return False;
  
  if (!client->has_sync)
    return False;

  if (client->sync_is_waiting)
    return False; 		/* XXX WRONG XXX */

  ewmh_sync_client_init_counter(client);

  lowval  = XSyncValueLow32 (client->sync_value);
  highval = XSyncValueHigh32 (client->sync_value);

  sync_value_increment (&client->sync_value);

  dbg("%s() delivering _NET_WM_SYNC_REQUEST\n", __func__);

  client_deliver_message(client, 
			 intern_atoms[XA_WM_PROTOCOLS], 
			 intern_atoms[NET_WM_SYNC_REQUEST], 
			 CurrentTime, lowval, highval,
			 0);
  
  client->sync_is_waiting = True;
  
  return True;
}

Bool sync_client_init_counter(Client *client)
{
  Wm *w = client->wm;

  XSyncAlarmAttributes values;
  Atom                 type;
  int                  format, result;
  long                 bytes_after, n_items;
  XID                 *value;

  if (!have_xsync) 
    return False;
  
  if (!client->has_sync)
    return False;

  result =  XGetWindowProperty (dpy, client->window, 
				intern_atoms[NET_WM_SYNC_REQUEST_COUNTER],
				0, 1024L,
				False, XA_CARDINAL,
				&type, &format, &n_items,
				&bytes_after, (unsigned char **)&value);

  if (result != Success || value == NULL || format != 32)
    {
      dbg("%s() _NET_WM_SYNC_REQUEST_COUNTER failed\n", __func__);
      if (value) XFree (value);
      return False;
    }

  dbg("%s() creating alarm\n", __func__);

  client->sync_counter = *value;

  XSyncIntsToValue (&client->sync_value, random(), 0);
  XSyncSetCounter (dpy, client->sync_counter, client->sync_value);

  sync_value_increment (&client->sync_value);

  values.events = True;
  values.trigger.counter    = client->sync_counter;
  values.trigger.wait_value = client->sync_value;
  values.trigger.value_type = XSyncAbsolute;
  values.trigger.test_type  = XSyncPositiveComparison;
  XSyncIntToValue (&values.delta, 1);
  values.events = True;

  /* Note that by default, the alarm increments the trigger value
   * when it fires until the condition (counter.value < trigger.value)
   * is FALSE again.
   */
  client->sync_alarm = XSyncCreateAlarm (dpy,
					      XSyncCACounter 
					      | XSyncCAValue 
					      | XSyncCAValueType 
					      | XSyncCATestType 
					      | XSyncCADelta 
					      | XSyncCAEvents,
					      &values);
  XSync (dpy, False);

  /* XXX untrap error here */

  return True;
}

#endif
