/* 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "config.h"

#include <stdio.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif

#include "libs/fvwmlib.h"
#include "fvwm.h"
#include "externs.h"
#include "cursor.h"
#include "functions.h"
#include "bindings.h"
#include "misc.h"
#include "screen.h"
#include "events.h"

Time managing_since;

Atom _XA_WM_SX;
Atom _XA_MANAGER;
Atom _XA_ATOM_PAIR;
Atom _XA_WM_COLORMAP_NOTIFY;
#define _XA_TARGETS   conversion_targets[0]
#define _XA_MULTIPLE  conversion_targets[1]
#define _XA_TIMESTAMP conversion_targets[2]
#define _XA_VERSION   conversion_targets[3]
#define MAX_TARGETS                      4

Atom conversion_targets[MAX_TARGETS];

long icccm_version[] = { 2, 0 };

void
SetupICCCM2 (Bool replace_wm)
{
  Window running_wm_win;
  XSetWindowAttributes attr;
  XEvent xev;
  XClientMessageEvent ev;
  char wm_sx[20];

  sprintf (wm_sx, "WM_S%lu", Scr.screen);
  _XA_WM_SX =     XInternAtom (dpy, wm_sx, False);
  _XA_MANAGER =   XInternAtom (dpy, "MANAGER", False);
  _XA_ATOM_PAIR = XInternAtom (dpy, "ATOM_PAIR", False);
  _XA_TARGETS =   XInternAtom (dpy, "TARGETS", False);
  _XA_MULTIPLE =  XInternAtom (dpy, "MULTIPLE", False);
  _XA_TIMESTAMP = XInternAtom (dpy, "TIMESTAMP", False);
  _XA_VERSION =   XInternAtom (dpy, "VERSION", False);
  _XA_WM_COLORMAP_NOTIFY = XInternAtom (dpy, "WM_COLORMAP_NOTIFY", False);

  /* Check for a running ICCCM 2.0 compliant WM */
  running_wm_win = XGetSelectionOwner (dpy, _XA_WM_SX);
  if (running_wm_win != None) {
    DBUG("SetupICCCM2", "another ICCCM 2.0 compliant WM is running");
    if (!replace_wm)
      {
	fvwm_msg(ERR, "SetupICCCM2",
	  "another ICCCM 2.0 compliant WM is running, try -replace");
	exit (1);
      }
    /* We need to know when the old manager is gone.
       Thus we wait until it destroys running_wm_win. */
    attr.event_mask = StructureNotifyMask;
    XChangeWindowAttributes (dpy, running_wm_win, CWEventMask, &attr);
  }

  /* add PropChange to NoFocusWin events */
  attr.event_mask = PropertyChangeMask;
  XChangeWindowAttributes (dpy, Scr.NoFocusWin, CWEventMask, &attr);

  /* We are not yet in the event loop, thus lastTimestamp will not
     be ready. Have to get a timestamp manually by provoking a
     PropertyNotify. */
  XChangeProperty (dpy, Scr.NoFocusWin, XA_WM_CLASS, XA_STRING, 8,
		   PropModeAppend, NULL, 0);
  XWindowEvent (dpy, Scr.NoFocusWin, PropertyChangeMask, &xev);
  attr.event_mask = NoEventMask;
  XChangeWindowAttributes (dpy, Scr.NoFocusWin, CWEventMask, &attr);

  managing_since = xev.xproperty.time;

  XSetSelectionOwner (dpy, _XA_WM_SX, Scr.NoFocusWin, managing_since);
  if (XGetSelectionOwner (dpy, _XA_WM_SX) != Scr.NoFocusWin) {
    fvwm_msg (ERR, "SetupICCCM2", "failed to acquire selection ownership");
    exit (1);
  }

  /* Announce ourself as the new wm */
  ev.type = ClientMessage;
  ev.window = Scr.Root;
  ev.message_type = _XA_MANAGER;
  ev.format = 32;
  ev.data.l[0] = managing_since;
  ev.data.l[1] = _XA_WM_SX;
  XSendEvent (dpy, Scr.Root, False, StructureNotifyMask, (XEvent*)&ev);

  if (running_wm_win != None) {
    /* Wait for the old wm to finish. */
    /* FIXME: need a timeout here. */
    DBUG("SetupICCCM2", "waiting for WM to give up");
    do {
      XWindowEvent (dpy, running_wm_win, StructureNotifyMask, &xev);
    } while (xev.type != DestroyNotify);
  }

  /* restore NoFocusWin event mask */
  attr.event_mask = XEVMASK_NOFOCUSW;
  XChangeWindowAttributes (dpy, Scr.NoFocusWin, CWEventMask, &attr);
}

/* We must make sure that we have released SubstructureRedirect
   before we destroy manager_win, so that another wm can start
   successfully. */
void
CloseICCCM2 (void)
{
  DBUG("CloseICCCM2", "good luck, new wm");
  XSelectInput(dpy, Scr.Root, NoEventMask);
  XSync(dpy, 0);
}

/* FIXME: property change actually succeeded */
static Bool
convertProperty (Window w, Atom target, Atom property)
{
  if (target == _XA_TARGETS)
    XChangeProperty (dpy, w, property,
		     XA_ATOM, 32, PropModeReplace,
		     (unsigned char *)conversion_targets, MAX_TARGETS);
  else if (target == _XA_TIMESTAMP)
    XChangeProperty (dpy, w, property,
		     XA_INTEGER, 32, PropModeReplace,
		     (unsigned char *)&managing_since, 1);
  else if (target == _XA_VERSION)
    XChangeProperty (dpy, w, property,
		     XA_INTEGER, 32, PropModeReplace,
		     (unsigned char *)icccm_version, 2);
  else return False;

  /* FIXME: This is ugly. We should rather select for
     PropertyNotify on the window, return to the main loop,
     and send the SelectionNotify once we are sure the property
     has arrived. Problem: this needs a list of pending
     SelectionNotifys. */
  XSync (dpy, 0);
  return True;
}

void
HandleSelectionRequest (void)
{
  Atom type, *adata;
  int i, format;
  unsigned long num, rest;
  unsigned char *data;
  XSelectionRequestEvent ev = Event.xselectionrequest;
  XSelectionEvent reply;

  reply.type = SelectionNotify;
  reply.display = dpy;
  reply.requestor = ev.requestor;
  reply.selection = ev.selection;
  reply.target = ev.target;
  reply.property = None;
  reply.time = ev.time;

  if (ev.target == _XA_MULTIPLE) {
    if (ev.property != None) {
      XGetWindowProperty (dpy, ev.requestor, ev.property, 0, 256, False,
			  _XA_ATOM_PAIR, &type, &format, &num, &rest, &data);
      /* FIXME: to be 100% correct, should deal with rest > 0,
	 but since we have 4 possible targets, we will hardly ever
	 meet multiple requests with a length > 8 */
      adata = (Atom*)data;
      for (i = 0; i < num; i += 2) {
	if (!convertProperty(ev.requestor, adata[i], adata[i+1]))
	  adata[i+1] = None;
      }
      XChangeProperty (dpy, ev.requestor, ev.property, _XA_ATOM_PAIR,
		       32, PropModeReplace, data, num);
      XFree (data);
    }
  } else {
    if (ev.property == None) ev.property = ev.target;
    if (convertProperty (ev.requestor, ev.target, ev.property))
      reply.property = ev.property;
  }

  XSync (dpy, 0);
  XSendEvent (dpy, ev.requestor, False, 0L, (XEvent*)&reply);
}

/* If another wm is requesting ownership of the selection,
   we receive a SelectionClear event. In that case, we have to
   release all resources and destroy manager_win. Done() calls
   CloseICCCM2() after undecorating all windows. */
void
HandleSelectionClear (void)
{
  DBUG("HandleSelectionClear", "I lost my selection!");
  Done(0, NULL);
}







syntax highlighted by Code2HTML, v. 0.9.1