/*
 * BRLTTY - A background process providing access to the console screen (when in
 *          text mode) for a blind person using a refreshable braille display.
 *
 * Copyright (C) 2019-2024 by Samuel Thibault <Samuel.Thibault@ens-lyon.org>
 *
 * BRLTTY comes with ABSOLUTELY NO WARRANTY.
 *
 * This is free software, placed under the terms of the
 * GNU Lesser General Public License, as published by the Free Software
 * Foundation; either version 2.1 of the License, or (at your option) any
 * later version. Please see the file LICENSE-LGPL for details.
 *
 * Web Page: http://brltty.app/
 *
 * This software is maintained by Dave Mielke <dave@mielke.cc>.
 */

#include "prologue.h"

#include <string.h>

#include "xsel.h"
#include <X11/Xatom.h>

#ifdef HAVE_X11_EXTENSIONS_XFIXES_H
#include <X11/extensions/Xfixes.h>
#else /* HAVE_X11_EXTENSIONS_XFIXES_H */
#warning clipboard tracking not supported by this build - check that libxfixes has been installed
#endif /* HAVE_X11_EXTENSIONS_XFIXES_H */

/* TODO: get initial clipboard value on startup */

void XSelInit(Display *dpy, XSelData *data) {
  data->sel = XInternAtom(dpy, "CLIPBOARD", False);
  data->selProp = XInternAtom(dpy, "BRLTTY_CLIPBOARD", False);
  data->incr = XInternAtom(dpy, "INCR", False);
  data->utf8 = XInternAtom(dpy,"UTF8_STRING",False);
  data->targetsAtom = XInternAtom(dpy,"TARGETS",False);
  data->selWindow = XCreateSimpleWindow(dpy, RootWindow(dpy, DefaultScreen(dpy)), -10, -10, 1, 1, 0, 0, 0);

#ifdef HAVE_X11_EXTENSIONS_XFIXES_H
  data->haveXfixes = XFixesQueryExtension(dpy, &data->xfixesEventBase, &data->xfixesErrorBase);
  if (data->haveXfixes) {
    XFixesSelectSelectionInput(dpy, data->selWindow, data->sel, XFixesSetSelectionOwnerNotifyMask);
  }
#endif /* HAVE_X11_EXTENSIONS_XFIXES_H */

}

void XSelSet(Display *dpy, XSelData *data) {
  XSetSelectionOwner(dpy, data->sel, data->selWindow, CurrentTime);
  XFlush(dpy);
}

int XSelProcess(Display *dpy, XSelData *data, XEvent *ev, const char *content, XSelUpdate update) {
#ifdef HAVE_X11_EXTENSIONS_XFIXES_H
  if (data->haveXfixes && ev->type == data->xfixesEventBase + XFixesSelectionNotify) {
    XFixesSelectionNotifyEvent *xfEvent = (XFixesSelectionNotifyEvent *) ev;
    if (xfEvent->subtype == XFixesSetSelectionOwnerNotify &&
	xfEvent->selection == data->sel &&
	xfEvent->owner != None &&
	xfEvent->owner != data->selWindow) {
      /* TODO: use TARGETS to support non-utf8 clients */
      XConvertSelection(dpy, data->sel, data->utf8, data->selProp, data->selWindow, xfEvent->selection_timestamp);
    }
    return 1;
  } else
#endif /* HAVE_X11_EXTENSIONS_XFIXES_H */

  switch (ev->type) {
    case SelectionNotify:
      if (ev->xselection.property != None) {
	Atom type;
	int format;
	unsigned long nitems, size, ignore;
	unsigned char *prop_ret;
	XGetWindowProperty(dpy, data->selWindow, data->selProp, 0, 0, False, AnyPropertyType, &type, &format, &nitems, &size, &prop_ret);
	XFree(prop_ret);
	if (type == data->incr) {
	  // large data, but INCR not supported yet
	} else if (size != 0) {
	  XGetWindowProperty(dpy, data->selWindow, data->selProp, 0, size, False, AnyPropertyType, &type, &format, &nitems, &ignore, &prop_ret);

	  update((char*) prop_ret, size);

	  XFree(prop_ret);
	  XDeleteProperty(dpy, data->selWindow, data->selProp);
	}
      }
      return 1;
    case SelectionClear:
      update(NULL, 0);
      return 1;
    case SelectionRequest: {
      XSelectionEvent sev;
      XSelectionRequestEvent *srev = (XSelectionRequestEvent*)&ev->xselectionrequest;
      if (content && srev->target == data->utf8) {
	XChangeProperty(dpy, srev->requestor, srev->property, data->utf8, 8, PropModeReplace, (unsigned char*) content, strlen(content));
	sev.property = srev->property;
      } else if (srev->target == data->targetsAtom) {
	Atom targets[] = { data->targetsAtom, data->utf8 };
	XChangeProperty(dpy, srev->requestor, srev->property, XA_ATOM, 32, PropModeReplace, (unsigned char*) targets, sizeof(targets)/sizeof(*targets));
	sev.property = srev->property;
      } else {
	sev.property = None;
      }
      sev.type = SelectionNotify;
      sev.requestor = srev->requestor;
      sev.selection = srev->selection;
      sev.target = srev->target;
      sev.time = srev->time;
      XSendEvent(dpy, srev->requestor, True, NoEventMask, (XEvent *) &sev);
      return 1;
    }
  }
  return 0;
}
