/*

Copyright 1991 by Mike Yang, mikey@sgi.com, Silicon Graphics, Inc.

Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation, and that the name of SGI not be used in advertising or
publicity pertaining to distribution of the software without specific,
written prior permission.  SGI makes no representations about the
suitability of this software for any purpose.  It is provided "as is"
without express or implied warranty.

*/

/*
 * This program demonstrates the use of XReparentWindow to visually
 * integrate existing windows from a different process into a toolkit
 * widget hierarchy.
 *
 * If you don't have Motif, comment out the "#define MOTIF" line to use
 * an Xaw widget.  Otherwise, a Motif text widget will be used for the
 * reparenting.
 *
 */

#define MOTIF           /* Use a Motif widget for the new parent */

#include <stdio.h>
#include <string.h>
#ifdef VMS
#include <decw$include/Xm.h>
#include <decw$include/Text.h>
#else
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <Xm/Xm.h>
/*
#include <X11/Xaw/AsciiText.h>
*/
/* Hack to make string defs work with both Motif and Xaw */
#define XmNwidth XtNwidth
#define XmNheight XtNheight
#define XmNx XtNx
#define XmNy XtNy
#define XmNbackground XtNbackground
#endif

Widget toplevel, parent;
Window theClient = NULL, theContainer;
int widthInc = 1, heightInc = 1, widthRemainder = 0, heightRemainder = 0;
int parentOffset;
char name[256];
Boolean found = False;
Atom WM_STATE;

static int count;
static Arg args[10];
static Boolean Xerror;

static int
ErrorHandler(dpy, event)
Display *dpy;
XErrorEvent *event;
{
  Xerror = True;
  return False;
}

void
resizeHandler()
{
  Dimension w, h;
  int width, height;

  count = 0;
  XtSetArg(args[count], XmNwidth, &w);  count++;
  XtSetArg(args[count], XmNheight, &h);  count++;
  XtGetValues(toplevel, args, count);
  if (widthInc != 1) {
    while (w % widthInc != widthRemainder) {
      w--;
    }
  }
  if (heightInc != 1) {
    while (h % heightInc != heightRemainder) {
      h--;
    }
  }
  width = w-2*parentOffset;
  height = h-2*parentOffset;
  if (width < 1) {
    width = 1;
  }
  if (height < 1) {
    height = 1;
  }
  XResizeWindow(XtDisplay(toplevel), theClient, width, height);
  XResizeWindow(XtDisplay(toplevel), theContainer, width, height);
}

void
resize_handler(w, client_data, event)
Widget w;
XtPointer client_data;
XEvent *event;
{
  if (event->type == ConfigureNotify) {
    resizeHandler();
  }
}

void
focus_handler(w, client_data, event)
Widget w;
XtPointer client_data;
XEvent *event;
{
  XErrorHandler old;

  Xerror = False;
  old = XSetErrorHandler((XErrorHandler) ErrorHandler);
  event->xfocus.window = theClient;
  XSendEvent(XtDisplay(toplevel), theClient, True, FocusChangeMask,
             event);
  XSetErrorHandler(old);
}

Window
findAClientWindow(window)
Window window;
{
  int rvalue, i;
  Atom actualtype;
  int actualformat;
  unsigned long nitems, bytesafter;
  unsigned char *propreturn;
  Window *children;
  unsigned int numchildren;
  Window returnroot, returnparent;
  Window result = NULL;
  XErrorHandler old;

  Xerror = False;
  old = XSetErrorHandler(ErrorHandler);
  rvalue = XGetWindowProperty(XtDisplay(toplevel), window, WM_STATE,
                              0, 1, False,
                              AnyPropertyType, &actualtype, &actualformat,
                              &nitems, &bytesafter, &propreturn);
  XSetErrorHandler(old);
  if (!Xerror && rvalue == Success && actualtype != None) {
    if (rvalue == Success) {
      XtFree((char *) propreturn);
    }
    return window;
  }

  old = XSetErrorHandler(ErrorHandler);
  if (!XQueryTree(XtDisplay(toplevel), window, &returnroot, &returnparent,
                  &children, &numchildren) || Xerror) {
    XSetErrorHandler(old);
    return NULL;
  }
  XSetErrorHandler(old);

  result = NULL;
  for (i=0; i<numchildren && !result ;i++) {
    result = findAClientWindow(children[i]);
  }
  if (numchildren) {
    XtFree((char *) children);
  }
  return result;
}

void
adjustClientResize()
{
  Position x, y;
  XWindowAttributes xwa;
  XSizeHints hints1, hints2;
  long ignore, flags;

  XGetWindowAttributes(XtDisplay(toplevel), theClient, &xwa);
  XGetWMNormalHints(XtDisplay(toplevel), theClient,
                    &hints1, &flags);
  if ((flags & PResizeInc) && hints1.width_inc && hints1.height_inc) {
    fprintf(stderr, "Preserving resize increments.\n");
    XGetWMNormalHints(XtDisplay(toplevel), XtWindow(toplevel),
                      &hints2, &ignore);
    hints2.flags |= PResizeInc;
    hints2.width_inc = hints1.width_inc;
    hints2.height_inc = hints1.height_inc;
/* Remember these remainders so that resizing occurs correctly */
    widthInc = hints1.width_inc;
    heightInc = hints1.height_inc;
    widthRemainder = (xwa.width+2*parentOffset) % widthInc;
    heightRemainder = (xwa.height+2*parentOffset) % heightInc;
    if (flags & PBaseSize) {
      count = 0;
      XtSetArg(args[count], XmNx, &x);  count++;
      XtSetArg(args[count], XmNy, &y);  count++;
      XtGetValues(parent, args, count);
      hints2.flags |= PBaseSize;
      hints2.base_width = x+hints1.base_width+2*parentOffset;
      hints2.base_height = y+hints1.base_height+2*parentOffset;
    }
    XSetWMNormalHints(XtDisplay(toplevel), XtWindow(toplevel),
                      &hints2);
  }
}

void
handleMappedClientWindow(Window client, Window wmparent)
{
  char *clientName;
  XWindowAttributes xwa;
  Dimension shadow, highlight;
  Window *children;
  unsigned int numchildren, each;
  Window returnroot, returnparent;
  XErrorHandler old;
  XSizeHints hints1, hints2;
  long ignore, flags;
  Pixel bg;

  if (client != wmparent) {
    XFetchName(XtDisplay(toplevel), client, &clientName);
  }
  if (client != wmparent && strcmp(clientName, name)) {
    fprintf(stderr, "Found window named %s.\n", clientName);
  } else {
    if (client != wmparent) {
      fprintf(stderr, "Got it (0x%x)!\n", client);
    } else {
      fprintf(stderr, "Assuming override-redirect window is the one.\n");
    }
    found = True;
#ifdef MOTIF
    count = 0;
    XtSetArg(args[count], XmNshadowThickness, &shadow);  count++;
    XtSetArg(args[count], XmNhighlightThickness, &highlight);  count++;
    XtGetValues(parent, args, count);
    parentOffset = shadow+highlight;
#else
    parentOffset = 0;
#endif
    theClient = client;
    XSelectInput(XtDisplay(toplevel), theClient, StructureNotifyMask);
    if (client != wmparent) {
      XGetWindowAttributes(XtDisplay(toplevel), wmparent, &xwa);
      XtMoveWidget(toplevel, xwa.x, xwa.y);
    }
    old = XSetErrorHandler(ErrorHandler);
    XGetWindowAttributes(XtDisplay(toplevel), client, &xwa);
    XtResizeWidget(toplevel, xwa.width+2*parentOffset, xwa.height+2*parentOffset, 0);
    XtResizeWidget(parent, xwa.width+2*parentOffset, xwa.height+2*parentOffset, 0);
    XtMapWidget(toplevel);
    adjustClientResize();

    XtAddEventHandler(toplevel, StructureNotifyMask, False,
                      (XtEventHandler) resize_handler,
                      (XtPointer) NULL);
    XtAddEventHandler(toplevel, FocusChangeMask, False,
                      (XtEventHandler) focus_handler,
                      (XtPointer) NULL);
    count = 0;
    XtSetArg(args[count], XmNbackground, &bg);  count++;
    XtGetValues(toplevel, args, count);
    theContainer = XCreateSimpleWindow(XtDisplay(toplevel), XtWindow(toplevel),
                                       parentOffset, parentOffset,
                                       xwa.width, xwa.height, 0, bg, bg);
    XMapWindow(XtDisplay(toplevel), theContainer);
    XReparentWindow(XtDisplay(toplevel), theClient, theContainer, 0, 0);
    if (!XQueryTree(XtDisplay(toplevel), client, &returnroot, &returnparent,
                    &children, &numchildren) || Xerror) {
      XSetErrorHandler(old);
      return;
    }
    if (numchildren > 0) {
      fprintf(stderr, "Reparenting %d children.\n", numchildren);
/* Stacking order is preserved since XQueryTree returns its children in
   bottommost to topmost order */
      for (each=0; each<numchildren; each++) {
        XGetWindowAttributes(XtDisplay(toplevel), children[each], &xwa);
        fprintf(stderr,
                "Reparenting child at offset %d and position %d, %d.\n",
                parentOffset, parentOffset+xwa.x, parentOffset+xwa.y);
        XReparentWindow(XtDisplay(toplevel), children[each], theContainer,
                        xwa.x, xwa.y);
      }
    }

    XSetErrorHandler(old);
  }
  if (client != wmparent) {
    XFree(clientName);
  }
}

void
handleRawEvent(event)
XEvent *event;
{
  Window client;

  if (!found) {
    if (event->type == MapNotify && !event->xmap.override_redirect &&
        (client = findAClientWindow(event->xmap.window))) {
      handleMappedClientWindow(client, event->xmap.window);
    } else if (event->type == MapNotify &&
               event->xmap.override_redirect &&
               event->xmap.window) {
      handleMappedClientWindow(event->xmap.window, event->xmap.window);
    }
  } else {
    if (event->type == DestroyNotify &&
        event->xdestroywindow.window == theClient) {
      fprintf(stderr, "Child killed - exiting.\n");
      XDestroyWindow(XtDisplay(toplevel), theContainer);
      XtDestroyWidget(toplevel);
      exit(0);
    }
  }
}


main(argc, argv)
int argc;
char **argv;
{
  XEvent event;

  printf("Name of client to reparent? ");
  fflush(stdout);
  fgets(name, sizeof(name), stdin);
  if (name[strlen(name)-1] == '\n') {
    name[strlen(name)-1] = '\0';
  }

  toplevel = XtInitialize(argv[0], "Reparent", NULL, 0,
                          (Cardinal *) &argc, argv);
  if (argc > 1) {
    fprintf(stderr, "Unknown option: %s.\n", argv[1]);
    exit(1);
  }
  count = 0;
#ifdef MOTIF
  XtSetArg(args[count], XmNshadowThickness, 5);  count++;
  XtSetArg(args[count], XmNhighlightThickness, 0);  count++;
  XtSetArg(args[count], XmNeditable, False);  count++;
  parent = XmCreateText(toplevel, "parent", args, count);
#else
  parent = XtCreateWidget("parent", asciiTextWidgetClass, toplevel,
                          args, count);
#endif
  XtManageChild(parent);
  XtSetMappedWhenManaged(toplevel, False);
  XtRealizeWidget(toplevel);
  
  XSelectInput(XtDisplay(toplevel),
               RootWindowOfScreen(XtScreen(toplevel)),
               SubstructureNotifyMask);
  WM_STATE = XInternAtom(XtDisplay(toplevel), "WM_STATE", False);

  fprintf(stderr, "Waiting for '%s' or the next override-redirect window.\n",
          name);

  while (True) {
    XtNextEvent(&event);
    if (!XtDispatchEvent(&event)) {
      handleRawEvent(&event);
    }
  }
}


