mirror of
https://github.com/Stichting-MINIX-Research-Foundation/xsrc.git
synced 2025-09-09 20:59:43 -04:00
2293 lines
64 KiB
C
2293 lines
64 KiB
C
/* $Xorg: Selection.c,v 1.4 2001/02/09 02:03:56 xorgcvs Exp $ */
|
|
|
|
/***********************************************************
|
|
Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
|
|
Copyright 1993 by Sun Microsystems, Inc. Mountain View, CA.
|
|
|
|
All Rights Reserved
|
|
|
|
Permission to use, copy, modify, and distribute this software and its
|
|
documentation for any purpose and without fee is hereby granted,
|
|
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 names of Digital or Sun not be
|
|
used in advertising or publicity pertaining to distribution of the
|
|
software without specific, written prior permission.
|
|
|
|
DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
|
|
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
|
|
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
|
|
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
|
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
|
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
|
SOFTWARE.
|
|
|
|
SUN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
|
|
NESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SUN BE LI-
|
|
ABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
|
|
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
|
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
|
|
THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
******************************************************************/
|
|
|
|
/*
|
|
|
|
Copyright 1987, 1988, 1994, 1998 The Open Group
|
|
|
|
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.
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of The Open Group shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from The Open Group.
|
|
|
|
*/
|
|
/* $XFree86: xc/lib/Xt/Selection.c,v 3.11 2004/05/05 00:07:03 dickey Exp $ */
|
|
|
|
#include "IntrinsicI.h"
|
|
#include "StringDefs.h"
|
|
#include "SelectionI.h"
|
|
#include <X11/Xatom.h>
|
|
#include <stdio.h>
|
|
|
|
void _XtSetDefaultSelectionTimeout(
|
|
unsigned long *timeout)
|
|
{
|
|
*timeout = 5000; /* default to 5 seconds */
|
|
}
|
|
|
|
void XtSetSelectionTimeout(
|
|
unsigned long timeout)
|
|
{
|
|
XtAppSetSelectionTimeout(_XtDefaultAppContext(), timeout);
|
|
}
|
|
|
|
void XtAppSetSelectionTimeout(
|
|
XtAppContext app,
|
|
unsigned long timeout)
|
|
{
|
|
LOCK_APP(app);
|
|
app->selectionTimeout = timeout;
|
|
UNLOCK_APP(app);
|
|
}
|
|
|
|
unsigned long XtGetSelectionTimeout(void)
|
|
{
|
|
return XtAppGetSelectionTimeout(_XtDefaultAppContext());
|
|
}
|
|
|
|
unsigned long XtAppGetSelectionTimeout(
|
|
XtAppContext app)
|
|
{
|
|
unsigned long retval;
|
|
|
|
LOCK_APP(app);
|
|
retval = app->selectionTimeout;
|
|
UNLOCK_APP(app);
|
|
return retval;
|
|
}
|
|
|
|
|
|
/* General utilities */
|
|
|
|
static void HandleSelectionReplies(Widget, XtPointer, XEvent *, Boolean *);
|
|
static void ReqTimedOut(XtPointer, XtIntervalId *);
|
|
static void HandlePropertyGone(Widget, XtPointer, XEvent *, Boolean *);
|
|
static void HandleGetIncrement(Widget, XtPointer, XEvent *, Boolean *);
|
|
static void HandleIncremental(Display *, Widget, Atom, CallBackInfo, unsigned long);
|
|
|
|
static XContext selectPropertyContext = 0;
|
|
static XContext paramPropertyContext = 0;
|
|
static XContext multipleContext = 0;
|
|
|
|
/* Multiple utilities */
|
|
static void AddSelectionRequests(Widget, Atom, int, Atom *, XtSelectionCallbackProc *, int, XtPointer *, Boolean *, Atom *);
|
|
static Boolean IsGatheringRequest(Widget, Atom);
|
|
|
|
#define PREALLOCED 32
|
|
|
|
/* Parameter utilities */
|
|
static void AddParamInfo(Widget, Atom, Atom);
|
|
static void RemoveParamInfo(Widget, Atom);
|
|
static Atom GetParamInfo(Widget, Atom);
|
|
|
|
static int StorageSize[3] = {1, sizeof(short), sizeof(long)};
|
|
#define BYTELENGTH(length, format) ((length) * StorageSize[(format)>>4])
|
|
#define NUMELEM(bytelength, format) ((bytelength) / StorageSize[(format)>>4])
|
|
|
|
/* Xlib and Xt are permitted to have different memory allocators, and in the
|
|
* XtSelectionCallbackProc the client is instructed to free the selection
|
|
* value with XtFree, so the selection value received from XGetWindowProperty
|
|
* should be copied to memory allocated through Xt. But copying is
|
|
* undesirable since the selection value may be large, and, under normal
|
|
* library configuration copying is unnecessary.
|
|
*/
|
|
#ifdef XTTRACEMEMORY
|
|
#define XT_COPY_SELECTION 1
|
|
#endif
|
|
|
|
/*ARGSUSED*/
|
|
static void FreePropList(
|
|
Widget w, /* unused */
|
|
XtPointer closure,
|
|
XtPointer callData) /* unused */
|
|
{
|
|
PropList sarray = (PropList)closure;
|
|
LOCK_PROCESS;
|
|
XDeleteContext(sarray->dpy, DefaultRootWindow(sarray->dpy),
|
|
selectPropertyContext);
|
|
UNLOCK_PROCESS;
|
|
XtFree((char*)sarray->list);
|
|
XtFree((char*)closure);
|
|
}
|
|
|
|
|
|
static PropList GetPropList(
|
|
Display *dpy)
|
|
{
|
|
PropList sarray;
|
|
Atom atoms[4];
|
|
static char* names[] = {
|
|
"INCR",
|
|
"MULTIPLE",
|
|
"TIMESTAMP",
|
|
"_XT_SELECTION_0" };
|
|
|
|
LOCK_PROCESS;
|
|
if (selectPropertyContext == 0)
|
|
selectPropertyContext = XUniqueContext();
|
|
if (XFindContext(dpy, DefaultRootWindow(dpy), selectPropertyContext,
|
|
(XPointer *)&sarray)) {
|
|
XtPerDisplay pd = _XtGetPerDisplay(dpy);
|
|
sarray = (PropList) __XtMalloc((unsigned) sizeof(PropListRec));
|
|
sarray->dpy = dpy;
|
|
XInternAtoms(dpy, names, 4, FALSE, atoms);
|
|
sarray->incr_atom = atoms[0];
|
|
sarray->indirect_atom = atoms[1];
|
|
sarray->timestamp_atom = atoms[2];
|
|
sarray->propCount = 1;
|
|
sarray->list =
|
|
(SelectionProp)__XtMalloc((unsigned) sizeof(SelectionPropRec));
|
|
sarray->list[0].prop = atoms[3];
|
|
sarray->list[0].avail = TRUE;
|
|
(void) XSaveContext(dpy, DefaultRootWindow(dpy), selectPropertyContext,
|
|
(char *) sarray);
|
|
_XtAddCallback( &pd->destroy_callbacks,
|
|
FreePropList, (XtPointer)sarray );
|
|
}
|
|
UNLOCK_PROCESS;
|
|
return sarray;
|
|
}
|
|
|
|
|
|
static Atom GetSelectionProperty(
|
|
Display *dpy)
|
|
{
|
|
SelectionProp p;
|
|
int propCount;
|
|
char propname[80];
|
|
PropList sarray = GetPropList(dpy);
|
|
|
|
for (p = sarray->list, propCount=sarray->propCount;
|
|
propCount;
|
|
p++, propCount--) {
|
|
if (p->avail) {
|
|
p->avail = FALSE;
|
|
return(p->prop);
|
|
}
|
|
}
|
|
propCount = sarray->propCount++;
|
|
sarray->list = (SelectionProp) XtRealloc((XtPointer)sarray->list,
|
|
(unsigned)(sarray->propCount*sizeof(SelectionPropRec)));
|
|
(void) sprintf(propname, "%s%d", "_XT_SELECTION_", propCount);
|
|
sarray->list[propCount].prop = XInternAtom(dpy, propname, FALSE);
|
|
sarray->list[propCount].avail = FALSE;
|
|
return(sarray->list[propCount].prop);
|
|
}
|
|
|
|
static void FreeSelectionProperty(
|
|
Display *dpy,
|
|
Atom prop)
|
|
{
|
|
SelectionProp p;
|
|
PropList sarray;
|
|
if (prop == None) return;
|
|
LOCK_PROCESS;
|
|
if (XFindContext(dpy, DefaultRootWindow(dpy), selectPropertyContext,
|
|
(XPointer *)&sarray))
|
|
XtAppErrorMsg(XtDisplayToApplicationContext(dpy),
|
|
"noSelectionProperties", "freeSelectionProperty", XtCXtToolkitError,
|
|
"internal error: no selection property context for display",
|
|
(String *)NULL, (Cardinal *)NULL );
|
|
UNLOCK_PROCESS;
|
|
for (p = sarray->list; p; p++)
|
|
if (p->prop == prop) {
|
|
p->avail = TRUE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void FreeInfo(
|
|
CallBackInfo info)
|
|
{
|
|
XtFree((char*)info->incremental);
|
|
XtFree((char*)info->callbacks);
|
|
XtFree((char*)info->req_closure);
|
|
XtFree((char*)info->target);
|
|
XtFree((char*)info);
|
|
}
|
|
|
|
static CallBackInfo MakeInfo(
|
|
Select ctx,
|
|
XtSelectionCallbackProc *callbacks,
|
|
XtPointer *closures,
|
|
int count,
|
|
Widget widget,
|
|
Time time,
|
|
Boolean *incremental,
|
|
Atom *properties)
|
|
{
|
|
CallBackInfo info = XtNew(CallBackInfoRec);
|
|
|
|
info->ctx = ctx;
|
|
info->callbacks = (XtSelectionCallbackProc *)
|
|
__XtMalloc((unsigned) (count * sizeof(XtSelectionCallbackProc)));
|
|
(void) memmove((char*)info->callbacks, (char*)callbacks,
|
|
count * sizeof(XtSelectionCallbackProc));
|
|
info->req_closure =
|
|
(XtPointer*)__XtMalloc((unsigned) (count * sizeof(XtPointer)));
|
|
(void) memmove((char*)info->req_closure, (char*)closures,
|
|
count * sizeof(XtPointer));
|
|
if (count == 1 && properties != NULL && properties[0] != None)
|
|
info->property = properties[0];
|
|
else {
|
|
info->property = GetSelectionProperty(XtDisplay(widget));
|
|
XDeleteProperty(XtDisplay(widget), XtWindow(widget),
|
|
info->property);
|
|
}
|
|
info->proc = HandleSelectionReplies;
|
|
info->widget = widget;
|
|
info->time = time;
|
|
info->incremental = (Boolean*) __XtMalloc(count * sizeof(Boolean));
|
|
(void) memmove((char*)info->incremental, (char*) incremental,
|
|
count * sizeof(Boolean));
|
|
info->current = 0;
|
|
info->value = NULL;
|
|
return (info);
|
|
}
|
|
|
|
static void RequestSelectionValue(
|
|
CallBackInfo info,
|
|
Atom selection,
|
|
Atom target)
|
|
{
|
|
#ifndef DEBUG_WO_TIMERS
|
|
XtAppContext app = XtWidgetToApplicationContext(info->widget);
|
|
info->timeout = XtAppAddTimeOut(app,
|
|
app->selectionTimeout, ReqTimedOut, (XtPointer)info);
|
|
#endif
|
|
XtAddEventHandler(info->widget, (EventMask)0, TRUE,
|
|
HandleSelectionReplies, (XtPointer)info);
|
|
XConvertSelection(info->ctx->dpy, selection, target,
|
|
info->property, XtWindow(info->widget), info->time);
|
|
}
|
|
|
|
|
|
static XContext selectContext = 0;
|
|
|
|
static Select NewContext(
|
|
Display *dpy,
|
|
Atom selection)
|
|
{
|
|
/* assert(selectContext != 0) */
|
|
Select ctx = XtNew(SelectRec);
|
|
ctx->dpy = dpy;
|
|
ctx->selection = selection;
|
|
ctx->widget = NULL;
|
|
ctx->prop_list = GetPropList(dpy);
|
|
ctx->ref_count = 0;
|
|
ctx->free_when_done = FALSE;
|
|
ctx->was_disowned = FALSE;
|
|
LOCK_PROCESS;
|
|
(void)XSaveContext(dpy, (Window)selection, selectContext, (char *)ctx);
|
|
UNLOCK_PROCESS;
|
|
return ctx;
|
|
}
|
|
|
|
static Select FindCtx(
|
|
Display *dpy,
|
|
Atom selection)
|
|
{
|
|
Select ctx;
|
|
|
|
LOCK_PROCESS;
|
|
if (selectContext == 0)
|
|
selectContext = XUniqueContext();
|
|
if (XFindContext(dpy, (Window)selection, selectContext, (XPointer *)&ctx))
|
|
ctx = NewContext(dpy, selection);
|
|
UNLOCK_PROCESS;
|
|
return ctx;
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static void WidgetDestroyed(
|
|
Widget widget,
|
|
XtPointer closure, XtPointer data)
|
|
{
|
|
Select ctx = (Select) closure;
|
|
if (ctx->widget == widget) {
|
|
if (ctx->free_when_done)
|
|
XtFree((char*)ctx);
|
|
else
|
|
ctx->widget = NULL;
|
|
}
|
|
}
|
|
|
|
/* Selection Owner code */
|
|
|
|
static void HandleSelectionEvents(Widget, XtPointer, XEvent *, Boolean *);
|
|
|
|
static Boolean LoseSelection(
|
|
Select ctx,
|
|
Widget widget,
|
|
Atom selection,
|
|
Time time)
|
|
{
|
|
if ((ctx->widget == widget) &&
|
|
(ctx->selection == selection) && /* paranoia */
|
|
!ctx->was_disowned &&
|
|
((time == CurrentTime) || (time >= ctx->time)))
|
|
{
|
|
XtRemoveEventHandler(widget, (EventMask)0, TRUE,
|
|
HandleSelectionEvents, (XtPointer)ctx);
|
|
XtRemoveCallback(widget, XtNdestroyCallback,
|
|
WidgetDestroyed, (XtPointer)ctx);
|
|
ctx->was_disowned = TRUE; /* widget officially loses ownership */
|
|
/* now inform widget */
|
|
if (ctx->loses) {
|
|
if (ctx->incremental)
|
|
(*(XtLoseSelectionIncrProc)ctx->loses)
|
|
(widget, &ctx->selection, ctx->owner_closure);
|
|
else (*ctx->loses)(widget, &ctx->selection);
|
|
}
|
|
return(TRUE);
|
|
}
|
|
else return(FALSE);
|
|
}
|
|
|
|
static XContext selectWindowContext = 0;
|
|
|
|
/* %%% Xlib.h should make this public! */
|
|
typedef int (*xErrorHandler)(Display*, XErrorEvent*);
|
|
|
|
static xErrorHandler oldErrorHandler = NULL;
|
|
static unsigned long firstProtectRequest;
|
|
static Window errorWindow;
|
|
|
|
static int LocalErrorHandler (
|
|
Display *dpy,
|
|
XErrorEvent *error)
|
|
{
|
|
int retval;
|
|
|
|
/* If BadWindow error on selection requestor, nothing to do but let
|
|
* the transfer timeout. Otherwise, invoke saved error handler. */
|
|
|
|
LOCK_PROCESS;
|
|
|
|
if (error->error_code == BadWindow && error->resourceid == errorWindow &&
|
|
error->serial >= firstProtectRequest) {
|
|
UNLOCK_PROCESS;
|
|
return 0;
|
|
}
|
|
|
|
if (oldErrorHandler == NULL) {
|
|
UNLOCK_PROCESS;
|
|
return 0; /* should never happen */
|
|
}
|
|
|
|
retval = (*oldErrorHandler)(dpy, error);
|
|
UNLOCK_PROCESS;
|
|
return retval;
|
|
}
|
|
|
|
static void StartProtectedSection(
|
|
Display *dpy,
|
|
Window window)
|
|
{
|
|
/* protect ourselves against request window being destroyed
|
|
* before completion of transfer */
|
|
|
|
LOCK_PROCESS;
|
|
oldErrorHandler = XSetErrorHandler(LocalErrorHandler);
|
|
firstProtectRequest = NextRequest(dpy);
|
|
errorWindow = window;
|
|
UNLOCK_PROCESS;
|
|
}
|
|
|
|
static void EndProtectedSection(
|
|
Display *dpy)
|
|
{
|
|
/* flush any generated errors on requestor and
|
|
* restore original error handler */
|
|
|
|
XSync(dpy, False);
|
|
|
|
LOCK_PROCESS;
|
|
XSetErrorHandler(oldErrorHandler);
|
|
oldErrorHandler = NULL;
|
|
UNLOCK_PROCESS;
|
|
}
|
|
|
|
static void AddHandler(
|
|
Request req,
|
|
EventMask mask,
|
|
XtEventHandler proc,
|
|
XtPointer closure)
|
|
{
|
|
Display *dpy = req->ctx->dpy;
|
|
Window window = req->requestor;
|
|
Widget widget = XtWindowToWidget(dpy, window);
|
|
|
|
if (widget != NULL) req->widget = widget;
|
|
else widget = req->widget;
|
|
|
|
if (XtWindow(widget) == window)
|
|
XtAddEventHandler(widget, mask, False, proc, closure);
|
|
else {
|
|
RequestWindowRec *requestWindowRec;
|
|
LOCK_PROCESS;
|
|
if (selectWindowContext == 0)
|
|
selectWindowContext = XUniqueContext();
|
|
if (XFindContext(dpy, window, selectWindowContext,
|
|
(XPointer *)&requestWindowRec)) {
|
|
requestWindowRec = XtNew(RequestWindowRec);
|
|
requestWindowRec->active_transfer_count = 0;
|
|
(void)XSaveContext(dpy, window, selectWindowContext,
|
|
(char *)requestWindowRec);
|
|
}
|
|
UNLOCK_PROCESS;
|
|
if (requestWindowRec->active_transfer_count++ == 0) {
|
|
XtRegisterDrawable(dpy, window, widget);
|
|
XSelectInput(dpy, window, mask);
|
|
}
|
|
XtAddRawEventHandler(widget, mask, FALSE, proc, closure);
|
|
}
|
|
}
|
|
|
|
static void RemoveHandler(
|
|
Request req,
|
|
EventMask mask,
|
|
XtEventHandler proc,
|
|
XtPointer closure)
|
|
{
|
|
Display *dpy = req->ctx->dpy;
|
|
Window window = req->requestor;
|
|
Widget widget = req->widget;
|
|
|
|
if ((XtWindowToWidget(dpy, window) == widget) &&
|
|
(XtWindow(widget) != window)) {
|
|
/* we had to hang this window onto our widget; take it off */
|
|
RequestWindowRec* requestWindowRec;
|
|
XtRemoveRawEventHandler(widget, mask, TRUE, proc, closure);
|
|
LOCK_PROCESS;
|
|
(void)XFindContext(dpy, window, selectWindowContext,
|
|
(XPointer *)&requestWindowRec);
|
|
UNLOCK_PROCESS;
|
|
if (--requestWindowRec->active_transfer_count == 0) {
|
|
XtUnregisterDrawable(dpy, window);
|
|
StartProtectedSection(dpy, window);
|
|
XSelectInput(dpy, window, 0L);
|
|
EndProtectedSection(dpy);
|
|
LOCK_PROCESS;
|
|
(void)XDeleteContext(dpy, window, selectWindowContext);
|
|
UNLOCK_PROCESS;
|
|
XtFree((char*)requestWindowRec);
|
|
}
|
|
} else {
|
|
XtRemoveEventHandler(widget, mask, TRUE, proc, closure);
|
|
}
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
static void OwnerTimedOut(
|
|
XtPointer closure,
|
|
XtIntervalId *id)
|
|
{
|
|
Request req = (Request)closure;
|
|
Select ctx = req->ctx;
|
|
|
|
if (ctx->incremental && (ctx->owner_cancel != NULL)) {
|
|
(*ctx->owner_cancel)(ctx->widget, &ctx->selection,
|
|
&req->target, (XtRequestId*)&req,
|
|
ctx->owner_closure);
|
|
} else {
|
|
if (ctx->notify == NULL)
|
|
XtFree((char*)req->value);
|
|
else {
|
|
/* the requestor hasn't deleted the property, but
|
|
* the owner needs to free the value.
|
|
*/
|
|
if (ctx->incremental)
|
|
(*(XtSelectionDoneIncrProc)ctx->notify)
|
|
(ctx->widget, &ctx->selection, &req->target,
|
|
(XtRequestId*)&req, ctx->owner_closure);
|
|
else
|
|
(*ctx->notify)(ctx->widget, &ctx->selection, &req->target);
|
|
}
|
|
}
|
|
|
|
RemoveHandler(req, (EventMask)PropertyChangeMask,
|
|
HandlePropertyGone, closure);
|
|
XtFree((char*)req);
|
|
if (--ctx->ref_count == 0 && ctx->free_when_done)
|
|
XtFree((char*)ctx);
|
|
}
|
|
|
|
static void SendIncrement(
|
|
Request incr)
|
|
{
|
|
Display *dpy = incr->ctx->dpy;
|
|
|
|
unsigned long incrSize = MAX_SELECTION_INCR(dpy);
|
|
if (incrSize > incr->bytelength - incr->offset)
|
|
incrSize = incr->bytelength - incr->offset;
|
|
StartProtectedSection(dpy, incr->requestor);
|
|
XChangeProperty(dpy, incr->requestor, incr->property,
|
|
incr->type, incr->format, PropModeReplace,
|
|
(unsigned char *)incr->value + incr->offset,
|
|
NUMELEM((int)incrSize, incr->format));
|
|
EndProtectedSection(dpy);
|
|
incr->offset += incrSize;
|
|
}
|
|
|
|
static void AllSent(
|
|
Request req)
|
|
{
|
|
Select ctx = req->ctx;
|
|
StartProtectedSection(ctx->dpy, req->requestor);
|
|
XChangeProperty(ctx->dpy, req->requestor,
|
|
req->property, req->type, req->format,
|
|
PropModeReplace, (unsigned char *) NULL, 0);
|
|
EndProtectedSection(ctx->dpy);
|
|
req->allSent = TRUE;
|
|
|
|
if (ctx->notify == NULL) XtFree((char*)req->value);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static void HandlePropertyGone(
|
|
Widget widget,
|
|
XtPointer closure,
|
|
XEvent *ev,
|
|
Boolean *cont)
|
|
{
|
|
XPropertyEvent *event = (XPropertyEvent *) ev;
|
|
Request req = (Request)closure;
|
|
Select ctx = req->ctx;
|
|
|
|
if ((event->type != PropertyNotify) ||
|
|
(event->state != PropertyDelete) ||
|
|
(event->atom != req->property) ||
|
|
(event->window != req->requestor))
|
|
return;
|
|
#ifndef DEBUG_WO_TIMERS
|
|
XtRemoveTimeOut(req->timeout);
|
|
#endif
|
|
if (req->allSent) {
|
|
if (ctx->notify) {
|
|
if (ctx->incremental) {
|
|
(*(XtSelectionDoneIncrProc)ctx->notify)
|
|
(ctx->widget, &ctx->selection, &req->target,
|
|
(XtRequestId*)&req, ctx->owner_closure);
|
|
}
|
|
else (*ctx->notify)(ctx->widget, &ctx->selection, &req->target);
|
|
}
|
|
RemoveHandler(req, (EventMask)PropertyChangeMask,
|
|
HandlePropertyGone, closure);
|
|
XtFree((char*)req);
|
|
if (--ctx->ref_count == 0 && ctx->free_when_done)
|
|
XtFree((char*)ctx);
|
|
} else { /* is this part of an incremental transfer? */
|
|
if (ctx->incremental) {
|
|
if (req->bytelength == 0)
|
|
AllSent(req);
|
|
else {
|
|
unsigned long size = MAX_SELECTION_INCR(ctx->dpy);
|
|
SendIncrement(req);
|
|
(*(XtConvertSelectionIncrProc)ctx->convert)
|
|
(ctx->widget, &ctx->selection, &req->target,
|
|
&req->type, &req->value,
|
|
&req->bytelength, &req->format,
|
|
&size, ctx->owner_closure, (XtPointer*)&req);
|
|
if (req->bytelength)
|
|
req->bytelength = BYTELENGTH(req->bytelength, req->format);
|
|
req->offset = 0;
|
|
}
|
|
} else {
|
|
if (req->offset < req->bytelength)
|
|
SendIncrement(req);
|
|
else AllSent(req);
|
|
}
|
|
#ifndef DEBUG_WO_TIMERS
|
|
{
|
|
XtAppContext app = XtWidgetToApplicationContext(req->widget);
|
|
req->timeout = XtAppAddTimeOut(app,
|
|
app->selectionTimeout, OwnerTimedOut, (XtPointer)req);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void PrepareIncremental(
|
|
Request req,
|
|
Widget widget,
|
|
Window window,
|
|
Atom property,
|
|
Atom target,
|
|
Atom targetType,
|
|
XtPointer value,
|
|
unsigned long length,
|
|
int format)
|
|
{
|
|
req->type = targetType;
|
|
req->value = value;
|
|
req->bytelength = BYTELENGTH(length,format);
|
|
req->format = format;
|
|
req->offset = 0;
|
|
req->target = target;
|
|
req->widget = widget;
|
|
req->allSent = FALSE;
|
|
#ifndef DEBUG_WO_TIMERS
|
|
{
|
|
XtAppContext app = XtWidgetToApplicationContext(widget);
|
|
req->timeout = XtAppAddTimeOut(app,
|
|
app->selectionTimeout, OwnerTimedOut, (XtPointer)req);
|
|
}
|
|
#endif
|
|
AddHandler(req, (EventMask)PropertyChangeMask,
|
|
HandlePropertyGone, (XtPointer)req);
|
|
/* now send client INCR property */
|
|
XChangeProperty(req->ctx->dpy, window, req->property,
|
|
req->ctx->prop_list->incr_atom,
|
|
32, PropModeReplace,
|
|
(unsigned char *)&req->bytelength, 1);
|
|
}
|
|
|
|
static Boolean GetConversion(
|
|
Select ctx, /* logical owner */
|
|
XSelectionRequestEvent* event,
|
|
Atom target,
|
|
Atom property, /* requestor's property */
|
|
Widget widget) /* physical owner (receives events) */
|
|
{
|
|
XtPointer value = NULL;
|
|
unsigned long length;
|
|
int format;
|
|
Atom targetType;
|
|
Request req = XtNew(RequestRec);
|
|
Boolean timestamp_target = (target == ctx->prop_list->timestamp_atom);
|
|
|
|
req->ctx = ctx;
|
|
req->event = *event;
|
|
req->property = property;
|
|
req->requestor = event->requestor;
|
|
|
|
if (timestamp_target) {
|
|
value = __XtMalloc(sizeof(long));
|
|
*(long*)value = ctx->time;
|
|
targetType = XA_INTEGER;
|
|
length = 1;
|
|
format = 32;
|
|
}
|
|
else {
|
|
ctx->ref_count++;
|
|
if (ctx->incremental == TRUE) {
|
|
unsigned long size = MAX_SELECTION_INCR(ctx->dpy);
|
|
if ((*(XtConvertSelectionIncrProc)ctx->convert)
|
|
(ctx->widget, &event->selection, &target,
|
|
&targetType, &value, &length, &format,
|
|
&size, ctx->owner_closure, (XtRequestId*)&req)
|
|
== FALSE) {
|
|
XtFree((char*)req);
|
|
ctx->ref_count--;
|
|
return(FALSE);
|
|
}
|
|
StartProtectedSection(ctx->dpy, event->requestor);
|
|
PrepareIncremental(req, widget, event->requestor, property,
|
|
target, targetType, value, length, format);
|
|
return(TRUE);
|
|
}
|
|
ctx->req = req;
|
|
if ((*ctx->convert)(ctx->widget, &event->selection, &target,
|
|
&targetType, &value, &length, &format) == FALSE) {
|
|
XtFree((char*)req);
|
|
ctx->req = NULL;
|
|
ctx->ref_count--;
|
|
return(FALSE);
|
|
}
|
|
ctx->req = NULL;
|
|
}
|
|
StartProtectedSection(ctx->dpy, event->requestor);
|
|
if (BYTELENGTH(length,format) <= (unsigned long) MAX_SELECTION_INCR(ctx->dpy)) {
|
|
if (! timestamp_target) {
|
|
if (ctx->notify != NULL) {
|
|
req->target = target;
|
|
req->widget = widget;
|
|
req->allSent = TRUE;
|
|
#ifndef DEBUG_WO_TIMERS
|
|
{
|
|
XtAppContext app = XtWidgetToApplicationContext(req->widget);
|
|
req->timeout = XtAppAddTimeOut(app,
|
|
app->selectionTimeout, OwnerTimedOut, (XtPointer)req);
|
|
}
|
|
#endif
|
|
AddHandler(req, (EventMask)PropertyChangeMask,
|
|
HandlePropertyGone, (XtPointer)req);
|
|
}
|
|
else ctx->ref_count--;
|
|
}
|
|
XChangeProperty(ctx->dpy, event->requestor, property,
|
|
targetType, format, PropModeReplace,
|
|
(unsigned char *)value, (int)length);
|
|
/* free storage for client if no notify proc */
|
|
if (timestamp_target || ctx->notify == NULL) {
|
|
XtFree((char*)value);
|
|
XtFree((char*)req);
|
|
}
|
|
} else {
|
|
PrepareIncremental(req, widget, event->requestor, property,
|
|
target, targetType, value, length, format);
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static void HandleSelectionEvents(
|
|
Widget widget,
|
|
XtPointer closure,
|
|
XEvent *event,
|
|
Boolean *cont)
|
|
{
|
|
Select ctx;
|
|
XSelectionEvent ev;
|
|
Atom target;
|
|
int count;
|
|
Boolean writeback = FALSE;
|
|
|
|
ctx = (Select) closure;
|
|
switch (event->type) {
|
|
case SelectionClear:
|
|
/* if this event is not for the selection we registered for,
|
|
* don't do anything */
|
|
if (ctx->selection != event->xselectionclear.selection ||
|
|
ctx->serial > event->xselectionclear.serial)
|
|
break;
|
|
(void) LoseSelection(ctx, widget, event->xselectionclear.selection,
|
|
event->xselectionclear.time);
|
|
break;
|
|
case SelectionRequest:
|
|
/* if this event is not for the selection we registered for,
|
|
* don't do anything */
|
|
if (ctx->selection != event->xselectionrequest.selection)
|
|
break;
|
|
ev.type = SelectionNotify;
|
|
ev.display = event->xselectionrequest.display;
|
|
ev.requestor = event->xselectionrequest.requestor;
|
|
ev.selection = event->xselectionrequest.selection;
|
|
ev.time = event->xselectionrequest.time;
|
|
ev.target = event->xselectionrequest.target;
|
|
if (event->xselectionrequest.property == None) /* obsolete requestor */
|
|
event->xselectionrequest.property = event->xselectionrequest.target;
|
|
if (ctx->widget != widget || ctx->was_disowned
|
|
|| ((event->xselectionrequest.time != CurrentTime)
|
|
&& (event->xselectionrequest.time < ctx->time)))
|
|
ev.property = None;
|
|
else {
|
|
if (ev.target == ctx->prop_list->indirect_atom) {
|
|
IndirectPair *p;
|
|
int format;
|
|
unsigned long bytesafter, length;
|
|
unsigned char *value;
|
|
ev.property = event->xselectionrequest.property;
|
|
StartProtectedSection(ev.display, ev.requestor);
|
|
(void) XGetWindowProperty(ev.display, ev.requestor,
|
|
event->xselectionrequest.property, 0L, 1000000,
|
|
False,(Atom)AnyPropertyType, &target, &format, &length,
|
|
&bytesafter, &value);
|
|
count = BYTELENGTH(length, format) / sizeof(IndirectPair);
|
|
for (p = (IndirectPair *)value; count; p++, count--) {
|
|
EndProtectedSection(ctx->dpy);
|
|
if (!GetConversion(ctx, (XSelectionRequestEvent*)event,
|
|
p->target, p->property, widget)) {
|
|
|
|
p->target = None;
|
|
writeback = TRUE;
|
|
StartProtectedSection(ctx->dpy, ev.requestor);
|
|
}
|
|
}
|
|
if (writeback)
|
|
XChangeProperty(ev.display, ev.requestor,
|
|
event->xselectionrequest.property, target,
|
|
format, PropModeReplace, value, (int)length);
|
|
XFree((char *)value);
|
|
} else /* not multiple */ {
|
|
if (GetConversion(ctx, (XSelectionRequestEvent*)event,
|
|
event->xselectionrequest.target,
|
|
event->xselectionrequest.property,
|
|
widget))
|
|
ev.property = event->xselectionrequest.property;
|
|
else {
|
|
ev.property = None;
|
|
StartProtectedSection(ctx->dpy, ev.requestor);
|
|
}
|
|
}
|
|
}
|
|
(void) XSendEvent(ctx->dpy, ev.requestor, False, (unsigned long)NULL,
|
|
(XEvent *) &ev);
|
|
|
|
EndProtectedSection(ctx->dpy);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
static Boolean OwnSelection(
|
|
Widget widget,
|
|
Atom selection,
|
|
Time time,
|
|
XtConvertSelectionProc convert,
|
|
XtLoseSelectionProc lose,
|
|
XtSelectionDoneProc notify,
|
|
XtCancelConvertSelectionProc cancel,
|
|
XtPointer closure,
|
|
Boolean incremental)
|
|
{
|
|
Select ctx;
|
|
Select oldctx = NULL;
|
|
|
|
if (!XtIsRealized(widget)) return False;
|
|
|
|
ctx = FindCtx(XtDisplay(widget), selection);
|
|
if (ctx->widget != widget || ctx->time != time ||
|
|
ctx->ref_count || ctx->was_disowned)
|
|
{
|
|
Boolean replacement = FALSE;
|
|
Window window = XtWindow(widget);
|
|
unsigned long serial = XNextRequest(ctx->dpy);
|
|
XSetSelectionOwner(ctx->dpy, selection, window, time);
|
|
if (XGetSelectionOwner(ctx->dpy, selection) != window)
|
|
return FALSE;
|
|
if (ctx->ref_count) { /* exchange is in-progress */
|
|
#ifdef DEBUG_ACTIVE
|
|
printf( "Active exchange for widget \"%s\"; selection=0x%lx, ref_count=%d\n",
|
|
XtName(widget), (long)selection, ctx->ref_count );
|
|
#endif
|
|
if (ctx->widget != widget ||
|
|
ctx->convert != convert ||
|
|
ctx->loses != lose ||
|
|
ctx->notify != notify ||
|
|
ctx->owner_cancel != cancel ||
|
|
ctx->incremental != incremental ||
|
|
ctx->owner_closure != closure)
|
|
{
|
|
if (ctx->widget == widget) {
|
|
XtRemoveEventHandler(widget, (EventMask)0, TRUE,
|
|
HandleSelectionEvents, (XtPointer)ctx);
|
|
XtRemoveCallback(widget, XtNdestroyCallback,
|
|
WidgetDestroyed, (XtPointer)ctx);
|
|
replacement = TRUE;
|
|
}
|
|
else if (!ctx->was_disowned) {
|
|
oldctx = ctx;
|
|
}
|
|
ctx->free_when_done = TRUE;
|
|
ctx = NewContext(XtDisplay(widget), selection);
|
|
}
|
|
else if (!ctx->was_disowned) { /* current owner is new owner */
|
|
ctx->time = time;
|
|
return TRUE;
|
|
}
|
|
}
|
|
if (ctx->widget != widget || ctx->was_disowned || replacement) {
|
|
if (ctx->widget && !ctx->was_disowned && !replacement) {
|
|
oldctx = ctx;
|
|
oldctx->free_when_done = TRUE;
|
|
ctx = NewContext(XtDisplay(widget), selection);
|
|
}
|
|
XtAddEventHandler(widget, (EventMask)0, TRUE,
|
|
HandleSelectionEvents, (XtPointer)ctx);
|
|
XtAddCallback(widget, XtNdestroyCallback,
|
|
WidgetDestroyed, (XtPointer)ctx);
|
|
}
|
|
ctx->widget = widget; /* Selection offically changes hands. */
|
|
ctx->time = time;
|
|
ctx->serial = serial;
|
|
}
|
|
ctx->convert = convert;
|
|
ctx->loses = lose;
|
|
ctx->notify = notify;
|
|
ctx->owner_cancel = cancel;
|
|
ctx->incremental = incremental;
|
|
ctx->owner_closure = closure;
|
|
ctx->was_disowned = FALSE;
|
|
|
|
/* Defer calling the previous selection owner's lose selection procedure
|
|
* until the new selection is established, to allow the previous
|
|
* selection owner to ask for the new selection to be converted in
|
|
* the lose selection procedure. The context pointer is the closure
|
|
* of the event handler and the destroy callback, so the old context
|
|
* pointer and the record contents must be preserved for LoseSelection.
|
|
*/
|
|
if (oldctx) {
|
|
(void) LoseSelection(oldctx, oldctx->widget, selection, oldctx->time);
|
|
if (!oldctx->ref_count && oldctx->free_when_done)
|
|
XtFree((char*)oldctx);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
Boolean XtOwnSelection(
|
|
Widget widget,
|
|
Atom selection,
|
|
Time time,
|
|
XtConvertSelectionProc convert,
|
|
XtLoseSelectionProc lose,
|
|
XtSelectionDoneProc notify)
|
|
{
|
|
Boolean retval;
|
|
WIDGET_TO_APPCON(widget);
|
|
|
|
LOCK_APP(app);
|
|
retval = OwnSelection(widget, selection, time, convert, lose, notify,
|
|
(XtCancelConvertSelectionProc)NULL,
|
|
(XtPointer)NULL, FALSE);
|
|
UNLOCK_APP(app);
|
|
return retval;
|
|
}
|
|
|
|
|
|
Boolean XtOwnSelectionIncremental(
|
|
Widget widget,
|
|
Atom selection,
|
|
Time time,
|
|
XtConvertSelectionIncrProc convert,
|
|
XtLoseSelectionIncrProc lose,
|
|
XtSelectionDoneIncrProc notify,
|
|
XtCancelConvertSelectionProc cancel,
|
|
XtPointer closure)
|
|
{
|
|
Boolean retval;
|
|
WIDGET_TO_APPCON(widget);
|
|
|
|
LOCK_APP(app);
|
|
retval = OwnSelection(widget, selection, time,
|
|
(XtConvertSelectionProc)convert,
|
|
(XtLoseSelectionProc)lose,
|
|
(XtSelectionDoneProc)notify,
|
|
cancel, closure, TRUE);
|
|
UNLOCK_APP(app);
|
|
return retval;
|
|
}
|
|
|
|
|
|
void XtDisownSelection(widget, selection, time)
|
|
Widget widget;
|
|
Atom selection;
|
|
Time time;
|
|
{
|
|
Select ctx;
|
|
WIDGET_TO_APPCON(widget);
|
|
|
|
LOCK_APP(app);
|
|
ctx = FindCtx(XtDisplay(widget), selection);
|
|
if (LoseSelection(ctx, widget, selection, time))
|
|
XSetSelectionOwner(XtDisplay(widget), selection, None, time);
|
|
UNLOCK_APP(app);
|
|
}
|
|
|
|
/* Selection Requestor code */
|
|
|
|
static Boolean IsINCRtype(
|
|
CallBackInfo info,
|
|
Window window,
|
|
Atom prop)
|
|
{
|
|
unsigned long bytesafter;
|
|
unsigned long length;
|
|
int format;
|
|
Atom type;
|
|
unsigned char *value;
|
|
|
|
if (prop == None) return False;
|
|
|
|
(void)XGetWindowProperty(XtDisplay(info->widget), window, prop, 0L, 0L,
|
|
False, info->ctx->prop_list->incr_atom,
|
|
&type, &format, &length, &bytesafter, &value);
|
|
|
|
return (type == info->ctx->prop_list->incr_atom);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static void ReqCleanup(
|
|
Widget widget,
|
|
XtPointer closure,
|
|
XEvent *ev,
|
|
Boolean *cont)
|
|
{
|
|
CallBackInfo info = (CallBackInfo)closure;
|
|
unsigned long bytesafter, length;
|
|
char *value;
|
|
int format;
|
|
Atom target;
|
|
|
|
if (ev->type == SelectionNotify) {
|
|
XSelectionEvent *event = (XSelectionEvent *) ev;
|
|
if (!MATCH_SELECT(event, info)) return; /* not really for us */
|
|
XtRemoveEventHandler(widget, (EventMask)0, TRUE,
|
|
ReqCleanup, (XtPointer) info );
|
|
if (IsINCRtype(info, XtWindow(widget), event->property)) {
|
|
info->proc = HandleGetIncrement;
|
|
XtAddEventHandler(info->widget, (EventMask) PropertyChangeMask,
|
|
FALSE, ReqCleanup, (XtPointer) info);
|
|
} else {
|
|
if (event->property != None)
|
|
XDeleteProperty(event->display, XtWindow(widget),
|
|
event->property);
|
|
FreeSelectionProperty(XtDisplay(widget), info->property);
|
|
FreeInfo(info);
|
|
}
|
|
} else if ((ev->type == PropertyNotify) &&
|
|
(ev->xproperty.state == PropertyNewValue) &&
|
|
(ev->xproperty.atom == info->property)) {
|
|
XPropertyEvent *event = (XPropertyEvent *) ev;
|
|
(void) XGetWindowProperty(event->display, XtWindow(widget),
|
|
event->atom, 0L, 1000000, True, AnyPropertyType,
|
|
&target, &format, &length, &bytesafter,
|
|
(unsigned char **) &value);
|
|
XFree(value);
|
|
if (length == 0) {
|
|
XtRemoveEventHandler(widget, (EventMask) PropertyChangeMask, FALSE,
|
|
ReqCleanup, (XtPointer) info );
|
|
FreeSelectionProperty(XtDisplay(widget), info->property);
|
|
XtFree(info->value); /* requestor never got this, so free now */
|
|
FreeInfo(info);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
static void ReqTimedOut(
|
|
XtPointer closure,
|
|
XtIntervalId *id)
|
|
{
|
|
XtPointer value = NULL;
|
|
unsigned long length = 0;
|
|
int format = 8;
|
|
Atom resulttype = XT_CONVERT_FAIL;
|
|
CallBackInfo info = (CallBackInfo)closure;
|
|
unsigned long bytesafter;
|
|
unsigned long proplength;
|
|
Atom type;
|
|
IndirectPair *pairs;
|
|
XtPointer *c;
|
|
int i;
|
|
|
|
if (*info->target == info->ctx->prop_list->indirect_atom) {
|
|
(void) XGetWindowProperty(XtDisplay(info->widget),
|
|
XtWindow(info->widget), info->property, 0L,
|
|
10000000, True, AnyPropertyType, &type, &format,
|
|
&proplength, &bytesafter, (unsigned char **) &pairs);
|
|
XFree((char*)pairs);
|
|
for (proplength = proplength / IndirectPairWordSize, i = 0, c = info->req_closure;
|
|
proplength; proplength--, c++, i++)
|
|
(*info->callbacks[i])(info->widget, *c,
|
|
&info->ctx->selection, &resulttype, value, &length, &format);
|
|
} else {
|
|
(*info->callbacks[0])(info->widget, *info->req_closure,
|
|
&info->ctx->selection, &resulttype, value, &length, &format);
|
|
}
|
|
|
|
/* change event handlers for straggler events */
|
|
if (info->proc == (XtEventHandler)HandleSelectionReplies) {
|
|
XtRemoveEventHandler(info->widget, (EventMask)0,
|
|
TRUE, info->proc, (XtPointer) info);
|
|
XtAddEventHandler(info->widget, (EventMask)0, TRUE,
|
|
ReqCleanup, (XtPointer) info);
|
|
} else {
|
|
XtRemoveEventHandler(info->widget,(EventMask) PropertyChangeMask,
|
|
FALSE, info->proc, (XtPointer) info);
|
|
XtAddEventHandler(info->widget, (EventMask) PropertyChangeMask,
|
|
FALSE, ReqCleanup, (XtPointer) info);
|
|
}
|
|
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static void HandleGetIncrement(
|
|
Widget widget,
|
|
XtPointer closure,
|
|
XEvent *ev,
|
|
Boolean *cont)
|
|
{
|
|
XPropertyEvent *event = (XPropertyEvent *) ev;
|
|
CallBackInfo info = (CallBackInfo) closure;
|
|
Select ctx = info->ctx;
|
|
char *value;
|
|
unsigned long bytesafter;
|
|
unsigned long length;
|
|
int bad;
|
|
int n = info->current;
|
|
|
|
if ((event->state != PropertyNewValue) || (event->atom != info->property))
|
|
return;
|
|
|
|
bad = XGetWindowProperty(event->display, XtWindow(widget),
|
|
event->atom, 0L,
|
|
10000000, True, AnyPropertyType, &info->type,
|
|
&info->format, &length, &bytesafter,
|
|
(unsigned char **) &value);
|
|
if (bad)
|
|
return;
|
|
#ifndef DEBUG_WO_TIMERS
|
|
XtRemoveTimeOut(info->timeout);
|
|
#endif
|
|
if (length == 0) {
|
|
unsigned long u_offset = NUMELEM(info->offset, info->format);
|
|
(*info->callbacks[n])(widget, *info->req_closure, &ctx->selection,
|
|
&info->type,
|
|
(info->offset == 0 ? value : info->value),
|
|
&u_offset, &info->format);
|
|
/* assert ((info->offset != 0) == (info->incremental[n]) */
|
|
if (info->offset != 0) XFree(value);
|
|
XtRemoveEventHandler(widget, (EventMask) PropertyChangeMask, FALSE,
|
|
HandleGetIncrement, (XtPointer) info);
|
|
FreeSelectionProperty(event->display, info->property);
|
|
FreeInfo(info);
|
|
} else { /* add increment to collection */
|
|
if (info->incremental[n]) {
|
|
#ifdef XT_COPY_SELECTION
|
|
int size = BYTELENGTH(length, info->format) + 1;
|
|
char *tmp = __XtMalloc((Cardinal) size);
|
|
(void) memmove(tmp, value, size);
|
|
XFree(value);
|
|
value = tmp;
|
|
#endif
|
|
(*info->callbacks[n])(widget, *info->req_closure, &ctx->selection,
|
|
&info->type, value, &length, &info->format);
|
|
} else {
|
|
int size = BYTELENGTH(length, info->format);
|
|
if (info->offset + size > info->bytelength) {
|
|
/* allocate enough for this and the next increment */
|
|
info->bytelength = info->offset + size * 2;
|
|
info->value = XtRealloc(info->value,
|
|
(Cardinal) info->bytelength);
|
|
}
|
|
(void) memmove(&info->value[info->offset], value, size);
|
|
info->offset += size;
|
|
XFree(value);
|
|
}
|
|
/* reset timer */
|
|
#ifndef DEBUG_WO_TIMERS
|
|
{
|
|
XtAppContext app = XtWidgetToApplicationContext(info->widget);
|
|
info->timeout = XtAppAddTimeOut(app,
|
|
app->selectionTimeout, ReqTimedOut, (XtPointer) info);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
static void HandleNone(
|
|
Widget widget,
|
|
XtSelectionCallbackProc callback,
|
|
XtPointer closure,
|
|
Atom selection)
|
|
{
|
|
unsigned long length = 0;
|
|
int format = 8;
|
|
Atom type = None;
|
|
|
|
(*callback)(widget, closure, &selection,
|
|
&type, NULL, &length, &format);
|
|
}
|
|
|
|
|
|
static long IncrPropSize(
|
|
Widget widget,
|
|
unsigned char* value,
|
|
int format,
|
|
unsigned long length)
|
|
{
|
|
unsigned long size;
|
|
if (format == 32) {
|
|
size = ((long*)value)[length-1]; /* %%% what order for longs? */
|
|
return size;
|
|
}
|
|
else {
|
|
XtAppWarningMsg( XtWidgetToApplicationContext(widget),
|
|
"badFormat","xtGetSelectionValue",XtCXtToolkitError,
|
|
"Selection owner returned type INCR property with format != 32",
|
|
(String*)NULL, (Cardinal*)NULL );
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static
|
|
Boolean HandleNormal(
|
|
Display *dpy,
|
|
Widget widget,
|
|
Atom property,
|
|
CallBackInfo info,
|
|
XtPointer closure,
|
|
Atom selection)
|
|
{
|
|
unsigned long bytesafter;
|
|
unsigned long length;
|
|
int format;
|
|
Atom type;
|
|
unsigned char *value;
|
|
int number = info->current;
|
|
|
|
(void) XGetWindowProperty(dpy, XtWindow(widget), property, 0L,
|
|
10000000, False, AnyPropertyType,
|
|
&type, &format, &length, &bytesafter, &value);
|
|
|
|
if (type == info->ctx->prop_list->incr_atom) {
|
|
unsigned long size = IncrPropSize(widget, value, format, length);
|
|
XFree((char *)value);
|
|
if (info->property != property) {
|
|
/* within MULTIPLE */
|
|
CallBackInfo ninfo;
|
|
ninfo = MakeInfo(info->ctx, &info->callbacks[number],
|
|
&info->req_closure[number], 1, widget,
|
|
info->time, &info->incremental[number], &property);
|
|
ninfo->target = (Atom *) __XtMalloc((unsigned) sizeof(Atom));
|
|
*ninfo->target = info->target[number + 1];
|
|
info = ninfo;
|
|
}
|
|
HandleIncremental(dpy, widget, property, info, size);
|
|
return FALSE;
|
|
}
|
|
|
|
XDeleteProperty(dpy, XtWindow(widget), property);
|
|
#ifdef XT_COPY_SELECTION
|
|
if (value) { /* it could have been deleted after the SelectionNotify */
|
|
int size = BYTELENGTH(length, info->format) + 1;
|
|
char *tmp = __XtMalloc((Cardinal) size);
|
|
(void) memmove(tmp, value, size);
|
|
XFree(value);
|
|
value = (unsigned char *) tmp;
|
|
}
|
|
#endif
|
|
(*info->callbacks[number])(widget, closure, &selection,
|
|
&type, (XtPointer)value, &length, &format);
|
|
|
|
if (info->incremental[number]) {
|
|
/* let requestor know the whole thing has been received */
|
|
value = (unsigned char*)__XtMalloc((unsigned)1);
|
|
length = 0;
|
|
(*info->callbacks[number])(widget, closure, &selection,
|
|
&type, (XtPointer)value, &length, &format);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void HandleIncremental(
|
|
Display *dpy,
|
|
Widget widget,
|
|
Atom property,
|
|
CallBackInfo info,
|
|
unsigned long size)
|
|
{
|
|
XtAddEventHandler(widget, (EventMask) PropertyChangeMask, FALSE,
|
|
HandleGetIncrement, (XtPointer) info);
|
|
|
|
/* now start the transfer */
|
|
XDeleteProperty(dpy, XtWindow(widget), property);
|
|
XFlush(dpy);
|
|
|
|
info->bytelength = size;
|
|
if (info->incremental[info->current]) /* requestor wants incremental too */
|
|
info->value = NULL; /* so no need for buffer to assemble value */
|
|
else
|
|
info->value = (char *) __XtMalloc((unsigned) info->bytelength);
|
|
info->offset = 0;
|
|
|
|
/* reset the timer */
|
|
info->proc = HandleGetIncrement;
|
|
#ifndef DEBUG_WO_TIMERS
|
|
{
|
|
XtAppContext app = XtWidgetToApplicationContext(info->widget);
|
|
info->timeout = XtAppAddTimeOut(app,
|
|
app->selectionTimeout, ReqTimedOut, (XtPointer) info);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static void HandleSelectionReplies(
|
|
Widget widget,
|
|
XtPointer closure,
|
|
XEvent *ev,
|
|
Boolean *cont)
|
|
{
|
|
XSelectionEvent *event = (XSelectionEvent *) ev;
|
|
Display *dpy = event->display;
|
|
CallBackInfo info = (CallBackInfo) closure;
|
|
Select ctx = info->ctx;
|
|
IndirectPair *pairs, *p;
|
|
unsigned long bytesafter;
|
|
unsigned long length;
|
|
int format;
|
|
Atom type;
|
|
XtPointer *c;
|
|
|
|
if (event->type != SelectionNotify) return;
|
|
if (!MATCH_SELECT(event, info)) return; /* not really for us */
|
|
#ifndef DEBUG_WO_TIMERS
|
|
XtRemoveTimeOut(info->timeout);
|
|
#endif
|
|
XtRemoveEventHandler(widget, (EventMask)0, TRUE,
|
|
HandleSelectionReplies, (XtPointer) info );
|
|
if (event->target == ctx->prop_list->indirect_atom) {
|
|
(void) XGetWindowProperty(dpy, XtWindow(widget), info->property, 0L,
|
|
10000000, True, AnyPropertyType, &type, &format,
|
|
&length, &bytesafter, (unsigned char **) &pairs);
|
|
for (length = length / IndirectPairWordSize, p = pairs,
|
|
c = info->req_closure;
|
|
length; length--, p++, c++, info->current++) {
|
|
if (event->property == None || format != 32 || p->target == None
|
|
|| /* bug compatibility */ p->property == None) {
|
|
HandleNone(widget, info->callbacks[info->current],
|
|
*c, event->selection);
|
|
if (p->property != None)
|
|
FreeSelectionProperty(XtDisplay(widget), p->property);
|
|
} else {
|
|
if (HandleNormal(dpy, widget, p->property, info, *c,
|
|
event->selection)) {
|
|
FreeSelectionProperty(XtDisplay(widget), p->property);
|
|
}
|
|
}
|
|
}
|
|
XFree((char*)pairs);
|
|
FreeSelectionProperty(dpy, info->property);
|
|
FreeInfo(info);
|
|
} else if (event->property == None) {
|
|
HandleNone(widget, info->callbacks[0], *info->req_closure, event->selection);
|
|
FreeSelectionProperty(XtDisplay(widget), info->property);
|
|
FreeInfo(info);
|
|
} else {
|
|
if (HandleNormal(dpy, widget, event->property, info,
|
|
*info->req_closure, event->selection)) {
|
|
FreeSelectionProperty(XtDisplay(widget), info->property);
|
|
FreeInfo(info);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void DoLocalTransfer(
|
|
Request req,
|
|
Atom selection,
|
|
Atom target,
|
|
Widget widget, /* The widget requesting the value. */
|
|
XtSelectionCallbackProc callback,
|
|
XtPointer closure, /* the closure for the callback, not the conversion */
|
|
Boolean incremental,
|
|
Atom property)
|
|
{
|
|
Select ctx = req->ctx;
|
|
XtPointer value = NULL, temp, total = NULL;
|
|
unsigned long length;
|
|
int format;
|
|
Atom resulttype;
|
|
unsigned long totallength = 0;
|
|
|
|
req->event.type = 0;
|
|
req->event.target = target;
|
|
req->event.property = req->property = property;
|
|
req->event.requestor = req->requestor = XtWindow(widget);
|
|
|
|
if (ctx->incremental) {
|
|
unsigned long size = MAX_SELECTION_INCR(ctx->dpy);
|
|
if (!(*(XtConvertSelectionIncrProc)ctx->convert)
|
|
(ctx->widget, &selection, &target,
|
|
&resulttype, &value, &length, &format,
|
|
&size, ctx->owner_closure, (XtRequestId*)&req)) {
|
|
HandleNone(widget, callback, closure, selection);
|
|
}
|
|
else {
|
|
if (incremental) {
|
|
Boolean allSent = FALSE;
|
|
while (!allSent) {
|
|
if (ctx->notify && (value != NULL)) {
|
|
int bytelength = BYTELENGTH(length,format);
|
|
/* both sides think they own this storage */
|
|
temp = __XtMalloc((unsigned)bytelength);
|
|
(void) memmove(temp, value, bytelength);
|
|
value = temp;
|
|
}
|
|
/* use care; older clients were never warned that
|
|
* they must return a value even if length==0
|
|
*/
|
|
if (value == NULL) value = __XtMalloc((unsigned)1);
|
|
(*callback)(widget, closure, &selection,
|
|
&resulttype, value, &length, &format);
|
|
if (length) {
|
|
/* should owner be notified on end-of-piece?
|
|
* Spec is unclear, but non-local transfers don't.
|
|
*/
|
|
(*(XtConvertSelectionIncrProc)ctx->convert)
|
|
(ctx->widget, &selection, &target,
|
|
&resulttype, &value, &length, &format,
|
|
&size, ctx->owner_closure,
|
|
(XtRequestId*)&req);
|
|
}
|
|
else allSent = TRUE;
|
|
}
|
|
} else {
|
|
while (length) {
|
|
int bytelength = BYTELENGTH(length, format);
|
|
total = XtRealloc(total,
|
|
(unsigned) (totallength += bytelength));
|
|
(void) memmove((char*)total + totallength - bytelength,
|
|
value,
|
|
bytelength);
|
|
(*(XtConvertSelectionIncrProc)ctx->convert)
|
|
(ctx->widget, &selection, &target,
|
|
&resulttype, &value, &length, &format,
|
|
&size, ctx->owner_closure, (XtRequestId*)&req);
|
|
}
|
|
if (total == NULL) total = __XtMalloc(1);
|
|
totallength = NUMELEM(totallength, format);
|
|
(*callback)(widget, closure, &selection, &resulttype,
|
|
total, &totallength, &format);
|
|
}
|
|
if (ctx->notify)
|
|
(*(XtSelectionDoneIncrProc)ctx->notify)
|
|
(ctx->widget, &selection, &target,
|
|
(XtRequestId*)&req, ctx->owner_closure);
|
|
else XtFree((char*)value);
|
|
}
|
|
} else { /* not incremental owner */
|
|
if (!(*ctx->convert)(ctx->widget, &selection, &target,
|
|
&resulttype, &value, &length, &format)) {
|
|
HandleNone(widget, callback, closure, selection);
|
|
} else {
|
|
if (ctx->notify && (value != NULL)) {
|
|
int bytelength = BYTELENGTH(length,format);
|
|
/* both sides think they own this storage; better copy */
|
|
temp = __XtMalloc((unsigned)bytelength);
|
|
(void) memmove(temp, value, bytelength);
|
|
value = temp;
|
|
}
|
|
if (value == NULL) value = __XtMalloc((unsigned)1);
|
|
(*callback)(widget, closure, &selection, &resulttype,
|
|
value, &length, &format);
|
|
if (ctx->notify)
|
|
(*ctx->notify)(ctx->widget, &selection, &target);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void GetSelectionValue(
|
|
Widget widget,
|
|
Atom selection,
|
|
Atom target,
|
|
XtSelectionCallbackProc callback,
|
|
XtPointer closure,
|
|
Time time,
|
|
Boolean incremental,
|
|
Atom property)
|
|
{
|
|
Select ctx;
|
|
CallBackInfo info;
|
|
Atom properties[1];
|
|
|
|
properties[0] = property;
|
|
|
|
ctx = FindCtx(XtDisplay(widget), selection);
|
|
if (ctx->widget && !ctx->was_disowned) {
|
|
RequestRec req;
|
|
ctx->req = &req;
|
|
req.ctx = ctx;
|
|
req.event.time = time;
|
|
ctx->ref_count++;
|
|
DoLocalTransfer(&req, selection, target, widget,
|
|
callback, closure, incremental, property);
|
|
if (--ctx->ref_count == 0 && ctx->free_when_done)
|
|
XtFree((char*)ctx);
|
|
else
|
|
ctx->req = NULL;
|
|
}
|
|
else {
|
|
info = MakeInfo(ctx, &callback, &closure, 1, widget,
|
|
time, &incremental, properties);
|
|
info->target = (Atom *)__XtMalloc((unsigned) sizeof(Atom));
|
|
*(info->target) = target;
|
|
RequestSelectionValue(info, selection, target);
|
|
}
|
|
}
|
|
|
|
|
|
void XtGetSelectionValue(
|
|
Widget widget,
|
|
Atom selection,
|
|
Atom target,
|
|
XtSelectionCallbackProc callback,
|
|
XtPointer closure,
|
|
Time time)
|
|
{
|
|
Atom property;
|
|
Boolean incr = False;
|
|
WIDGET_TO_APPCON(widget);
|
|
|
|
LOCK_APP(app);
|
|
property = GetParamInfo(widget, selection);
|
|
RemoveParamInfo(widget, selection);
|
|
|
|
if (IsGatheringRequest(widget, selection)) {
|
|
AddSelectionRequests(widget, selection, 1, &target, &callback, 1,
|
|
&closure, &incr, &property);
|
|
} else {
|
|
GetSelectionValue(widget, selection, target, callback,
|
|
closure, time, FALSE, property);
|
|
}
|
|
UNLOCK_APP(app);
|
|
}
|
|
|
|
|
|
void XtGetSelectionValueIncremental(
|
|
Widget widget,
|
|
Atom selection,
|
|
Atom target,
|
|
XtSelectionCallbackProc callback,
|
|
XtPointer closure,
|
|
Time time)
|
|
{
|
|
Atom property;
|
|
Boolean incr = TRUE;
|
|
WIDGET_TO_APPCON(widget);
|
|
|
|
LOCK_APP(app);
|
|
property = GetParamInfo(widget, selection);
|
|
RemoveParamInfo(widget, selection);
|
|
|
|
if (IsGatheringRequest(widget, selection)) {
|
|
AddSelectionRequests(widget, selection, 1, &target, &callback, 1,
|
|
&closure, &incr, &property);
|
|
} else {
|
|
GetSelectionValue(widget, selection, target, callback,
|
|
closure, time, TRUE, property);
|
|
}
|
|
|
|
UNLOCK_APP(app);
|
|
}
|
|
|
|
|
|
static void GetSelectionValues(
|
|
Widget widget,
|
|
Atom selection,
|
|
Atom *targets,
|
|
int count,
|
|
XtSelectionCallbackProc *callbacks,
|
|
int num_callbacks,
|
|
XtPointer *closures,
|
|
Time time,
|
|
Boolean *incremental,
|
|
Atom *properties)
|
|
{
|
|
Select ctx;
|
|
CallBackInfo info;
|
|
IndirectPair *pairs, *p;
|
|
Atom *t;
|
|
|
|
if (count == 0) return;
|
|
ctx = FindCtx(XtDisplay(widget), selection);
|
|
if (ctx->widget && !ctx->was_disowned) {
|
|
int j, i;
|
|
RequestRec req;
|
|
ctx->req = &req;
|
|
req.ctx = ctx;
|
|
req.event.time = time;
|
|
ctx->ref_count++;
|
|
for (i = 0, j = 0; count; count--, i++, j++ ) {
|
|
if (j >= num_callbacks) j = 0;
|
|
|
|
DoLocalTransfer(&req, selection, targets[i], widget,
|
|
callbacks[j], closures[i], incremental[i],
|
|
properties ? properties[i] : None);
|
|
|
|
}
|
|
if (--ctx->ref_count == 0 && ctx->free_when_done)
|
|
XtFree((char*)ctx);
|
|
else
|
|
ctx->req = NULL;
|
|
} else {
|
|
XtSelectionCallbackProc *passed_callbacks;
|
|
XtSelectionCallbackProc stack_cbs[32];
|
|
int i = 0, j = 0;
|
|
|
|
passed_callbacks = (XtSelectionCallbackProc *)
|
|
XtStackAlloc(sizeof(XtSelectionCallbackProc) * count, stack_cbs);
|
|
|
|
/* To deal with the old calls from XtGetSelectionValues* we
|
|
will repeat however many callbacks have been passed into
|
|
the array */
|
|
for(i = 0; i < count; i++) {
|
|
if (j >= num_callbacks) j = 0;
|
|
passed_callbacks[i] = callbacks[j];
|
|
j++;
|
|
}
|
|
info = MakeInfo(ctx, passed_callbacks, closures, count, widget,
|
|
time, incremental, properties);
|
|
XtStackFree((XtPointer) passed_callbacks, stack_cbs);
|
|
|
|
info->target = (Atom *)__XtMalloc((unsigned) ((count+1) * sizeof(Atom)));
|
|
(*info->target) = ctx->prop_list->indirect_atom;
|
|
(void) memmove((char *) info->target+sizeof(Atom), (char *) targets,
|
|
count * sizeof(Atom));
|
|
pairs = (IndirectPair*)__XtMalloc((unsigned)(count*sizeof(IndirectPair)));
|
|
for (p = &pairs[count-1], t = &targets[count-1], i = count - 1;
|
|
p >= pairs; p--, t--, i--) {
|
|
p->target = *t;
|
|
if (properties == NULL || properties[i] == None) {
|
|
p->property = GetSelectionProperty(XtDisplay(widget));
|
|
XDeleteProperty(XtDisplay(widget), XtWindow(widget),
|
|
p->property);
|
|
} else {
|
|
p->property = properties[i];
|
|
}
|
|
}
|
|
XChangeProperty(XtDisplay(widget), XtWindow(widget),
|
|
info->property, info->property,
|
|
32, PropModeReplace, (unsigned char *) pairs,
|
|
count * IndirectPairWordSize);
|
|
XtFree((char*)pairs);
|
|
RequestSelectionValue(info, selection, ctx->prop_list->indirect_atom);
|
|
}
|
|
}
|
|
|
|
|
|
void XtGetSelectionValues(
|
|
Widget widget,
|
|
Atom selection,
|
|
Atom *targets,
|
|
int count,
|
|
XtSelectionCallbackProc callback,
|
|
XtPointer *closures,
|
|
Time time)
|
|
{
|
|
Boolean incremental_values[32];
|
|
Boolean *incremental;
|
|
int i;
|
|
WIDGET_TO_APPCON(widget);
|
|
|
|
LOCK_APP(app);
|
|
incremental = XtStackAlloc(count * sizeof(Boolean), incremental_values);
|
|
for(i = 0; i < count; i++) incremental[i] = FALSE;
|
|
if (IsGatheringRequest(widget, selection)) {
|
|
AddSelectionRequests(widget, selection, count, targets, &callback,
|
|
1, closures, incremental, NULL);
|
|
} else {
|
|
GetSelectionValues(widget, selection, targets, count, &callback, 1,
|
|
closures, time, incremental, NULL);
|
|
}
|
|
XtStackFree((XtPointer) incremental, incremental_values);
|
|
UNLOCK_APP(app);
|
|
}
|
|
|
|
|
|
void XtGetSelectionValuesIncremental(
|
|
Widget widget,
|
|
Atom selection,
|
|
Atom *targets,
|
|
int count,
|
|
XtSelectionCallbackProc callback,
|
|
XtPointer *closures,
|
|
Time time)
|
|
{
|
|
Boolean incremental_values[32];
|
|
Boolean *incremental;
|
|
int i;
|
|
WIDGET_TO_APPCON(widget);
|
|
|
|
LOCK_APP(app);
|
|
incremental = XtStackAlloc(count * sizeof(Boolean), incremental_values);
|
|
for(i = 0; i < count; i++) incremental[i] = TRUE;
|
|
if (IsGatheringRequest(widget, selection)) {
|
|
AddSelectionRequests(widget, selection, count, targets, &callback,
|
|
1, closures, incremental, NULL);
|
|
} else {
|
|
GetSelectionValues(widget, selection, targets, count,
|
|
&callback, 1, closures, time, incremental, NULL);
|
|
}
|
|
XtStackFree((XtPointer) incremental, incremental_values);
|
|
UNLOCK_APP(app);
|
|
}
|
|
|
|
|
|
static Request GetRequestRecord(
|
|
Widget widget,
|
|
Atom selection,
|
|
XtRequestId id)
|
|
{
|
|
Request req = (Request)id;
|
|
Select ctx = NULL;
|
|
|
|
if ( (req == NULL
|
|
&& ((ctx = FindCtx( XtDisplay(widget), selection )) == NULL
|
|
|| ctx->req == NULL
|
|
|| ctx->selection != selection
|
|
|| ctx->widget == NULL))
|
|
|| (req != NULL
|
|
&& (req->ctx == NULL
|
|
|| req->ctx->selection != selection
|
|
|| req->ctx->widget != widget)))
|
|
{
|
|
String params = XtName(widget);
|
|
Cardinal num_params = 1;
|
|
XtAppWarningMsg(XtWidgetToApplicationContext(widget),
|
|
"notInConvertSelection", "xtGetSelectionRequest",
|
|
XtCXtToolkitError,
|
|
"XtGetSelectionRequest or XtGetSelectionParameters called for widget \"%s\" outside of ConvertSelection proc",
|
|
¶ms, &num_params
|
|
);
|
|
return NULL;
|
|
}
|
|
|
|
if (req == NULL) {
|
|
/* non-incremental owner; only one request can be
|
|
* outstanding at a time, so it's safe to keep ptr in ctx */
|
|
req = ctx->req;
|
|
}
|
|
return req;
|
|
}
|
|
|
|
XSelectionRequestEvent *XtGetSelectionRequest(
|
|
Widget widget,
|
|
Atom selection,
|
|
XtRequestId id)
|
|
{
|
|
Request req = (Request)id;
|
|
WIDGET_TO_APPCON(widget);
|
|
|
|
LOCK_APP(app);
|
|
|
|
req = GetRequestRecord(widget, selection, id);
|
|
|
|
if (! req) {
|
|
UNLOCK_APP(app);
|
|
return (XSelectionRequestEvent*) NULL;
|
|
}
|
|
|
|
if (req->event.type == 0) {
|
|
/* owner is local; construct the remainder of the event */
|
|
req->event.type = SelectionRequest;
|
|
req->event.serial = LastKnownRequestProcessed(XtDisplay(widget));
|
|
req->event.send_event = True;
|
|
req->event.display = XtDisplay(widget);
|
|
req->event.owner = XtWindow(req->ctx->widget);
|
|
req->event.selection = selection;
|
|
}
|
|
UNLOCK_APP(app);
|
|
return &req->event;
|
|
}
|
|
|
|
/* Property atom access */
|
|
Atom XtReservePropertyAtom(
|
|
Widget w)
|
|
{
|
|
return(GetSelectionProperty(XtDisplay(w)));
|
|
}
|
|
|
|
void XtReleasePropertyAtom(
|
|
Widget w,
|
|
Atom atom)
|
|
{
|
|
FreeSelectionProperty(XtDisplay(w), atom);
|
|
}
|
|
|
|
|
|
/* Multiple utilities */
|
|
|
|
/* All requests are put in a single list per widget. It is
|
|
very unlikely anyone will be gathering multiple MULTIPLE
|
|
requests at the same time, so the loss in efficiency for
|
|
this case is acceptable */
|
|
|
|
/* Queue one or more requests to the one we're gathering */
|
|
static void AddSelectionRequests(
|
|
Widget wid,
|
|
Atom sel,
|
|
int count,
|
|
Atom *targets,
|
|
XtSelectionCallbackProc *callbacks,
|
|
int num_cb,
|
|
XtPointer *closures,
|
|
Boolean *incrementals,
|
|
Atom *properties)
|
|
{
|
|
QueuedRequestInfo qi;
|
|
Window window = XtWindow(wid);
|
|
Display *dpy = XtDisplay(wid);
|
|
|
|
LOCK_PROCESS;
|
|
if (multipleContext == 0) multipleContext = XUniqueContext();
|
|
|
|
qi = NULL;
|
|
(void) XFindContext(dpy, window, multipleContext, (XPointer*) &qi);
|
|
|
|
if (qi != NULL) {
|
|
QueuedRequest *req = qi->requests;
|
|
int start = qi->count;
|
|
int i = 0;
|
|
int j = 0;
|
|
|
|
qi->count += count;
|
|
req = (QueuedRequest*) XtRealloc((char*) req,
|
|
(start + count) *
|
|
sizeof(QueuedRequest));
|
|
while(i < count) {
|
|
QueuedRequest newreq = (QueuedRequest)
|
|
__XtMalloc(sizeof(QueuedRequestRec));
|
|
newreq->selection = sel;
|
|
newreq->target = targets[i];
|
|
if (properties != NULL)
|
|
newreq->param = properties[i];
|
|
else {
|
|
newreq->param = GetSelectionProperty(dpy);
|
|
XDeleteProperty(dpy, window, newreq->param);
|
|
}
|
|
newreq->callback = callbacks[j];
|
|
newreq->closure = closures[i];
|
|
newreq->incremental = incrementals[i];
|
|
|
|
req[start] = newreq;
|
|
start++;
|
|
i++;
|
|
j++;
|
|
if (j > num_cb) j = 0;
|
|
}
|
|
|
|
qi->requests = req;
|
|
} else {
|
|
/* Impossible */
|
|
}
|
|
|
|
UNLOCK_PROCESS;
|
|
}
|
|
|
|
/* Only call IsGatheringRequest when we have a lock already */
|
|
|
|
static Boolean IsGatheringRequest(
|
|
Widget wid,
|
|
Atom sel)
|
|
{
|
|
QueuedRequestInfo qi;
|
|
Window window = XtWindow(wid);
|
|
Display *dpy = XtDisplay(wid);
|
|
Boolean found = False;
|
|
int i;
|
|
|
|
if (multipleContext == 0) multipleContext = XUniqueContext();
|
|
|
|
qi = NULL;
|
|
(void) XFindContext(dpy, window, multipleContext, (XPointer*) &qi);
|
|
|
|
if (qi != NULL) {
|
|
i = 0;
|
|
while(qi->selections[i] != None) {
|
|
if (qi->selections[i] == sel) {
|
|
found = True;
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
return(found);
|
|
}
|
|
|
|
/* Cleanup request scans the request queue and releases any
|
|
properties queued, and removes any requests queued */
|
|
static void CleanupRequest(
|
|
Display *dpy,
|
|
QueuedRequestInfo qi,
|
|
Atom sel)
|
|
{
|
|
int i, j, n;
|
|
|
|
i = 0;
|
|
|
|
/* Remove this selection from the list */
|
|
n = 0;
|
|
while(qi->selections[n] != sel &&
|
|
qi->selections[n] != None) n++;
|
|
if (qi->selections[n] == sel) {
|
|
while(qi->selections[n] != None) {
|
|
qi->selections[n] = qi->selections[n + 1];
|
|
n++;
|
|
}
|
|
}
|
|
|
|
while(i < qi->count) {
|
|
QueuedRequest req = qi->requests[i];
|
|
|
|
if (req->selection == sel) {
|
|
/* Match */
|
|
if (req->param != None)
|
|
FreeSelectionProperty(dpy, req->param);
|
|
qi->count--;
|
|
|
|
for(j = i; j < qi->count; j++)
|
|
qi->requests[j] = qi->requests[j + 1];
|
|
|
|
XtFree((char*) req);
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
extern void XtCreateSelectionRequest(
|
|
Widget widget,
|
|
Atom selection)
|
|
{
|
|
QueuedRequestInfo queueInfo;
|
|
Window window = XtWindow(widget);
|
|
Display *dpy = XtDisplay(widget);
|
|
int n;
|
|
|
|
LOCK_PROCESS;
|
|
if (multipleContext == 0) multipleContext = XUniqueContext();
|
|
|
|
queueInfo = NULL;
|
|
(void) XFindContext(dpy, window, multipleContext, (XPointer*) &queueInfo);
|
|
|
|
/* If there is one, then cancel it */
|
|
if (queueInfo != 0)
|
|
CleanupRequest(dpy, queueInfo, selection);
|
|
else {
|
|
/* Create it */
|
|
queueInfo = (QueuedRequestInfo) __XtMalloc(sizeof(QueuedRequestInfoRec));
|
|
queueInfo->count = 0;
|
|
queueInfo->selections = (Atom*) __XtMalloc(sizeof(Atom) * 2);
|
|
queueInfo->selections[0] = None;
|
|
queueInfo->requests = (QueuedRequest *)
|
|
__XtMalloc(sizeof(QueuedRequest));
|
|
}
|
|
|
|
/* Append this selection to list */
|
|
n = 0;
|
|
while(queueInfo->selections[n] != None) n++;
|
|
queueInfo->selections =
|
|
(Atom*) XtRealloc((char*) queueInfo->selections,
|
|
(n + 2) * sizeof(Atom));
|
|
queueInfo->selections[n] = selection;
|
|
queueInfo->selections[n + 1] = None;
|
|
|
|
(void) XSaveContext(dpy, window, multipleContext, (char*) queueInfo);
|
|
UNLOCK_PROCESS;
|
|
}
|
|
|
|
extern void XtSendSelectionRequest(
|
|
Widget widget,
|
|
Atom selection,
|
|
Time time)
|
|
{
|
|
QueuedRequestInfo queueInfo;
|
|
Window window = XtWindow(widget);
|
|
Display *dpy = XtDisplay(widget);
|
|
|
|
LOCK_PROCESS;
|
|
if (multipleContext == 0) multipleContext = XUniqueContext();
|
|
|
|
queueInfo = NULL;
|
|
(void) XFindContext(dpy, window, multipleContext, (XPointer*) &queueInfo);
|
|
if (queueInfo != NULL) {
|
|
int count = 0;
|
|
int i;
|
|
QueuedRequest *req = queueInfo->requests;
|
|
|
|
/* Construct the requests and send it using
|
|
GetSelectionValues */
|
|
for(i = 0; i < queueInfo->count; i++)
|
|
if (req[i]->selection == selection) count++;
|
|
|
|
if (count > 0) {
|
|
if (count == 1) {
|
|
for(i = 0; i < queueInfo->count; i++)
|
|
if (req[i]->selection == selection) break;
|
|
|
|
/* special case a multiple which isn't needed */
|
|
GetSelectionValue(widget, selection, req[i]->target,
|
|
req[i]->callback, req[i]->closure, time,
|
|
req[i]->incremental, req[i]->param);
|
|
} else {
|
|
Atom *targets;
|
|
Atom t[PREALLOCED];
|
|
XtSelectionCallbackProc *cbs;
|
|
XtSelectionCallbackProc c[PREALLOCED];
|
|
XtPointer *closures;
|
|
XtPointer cs[PREALLOCED];
|
|
Boolean *incrs;
|
|
Boolean ins[PREALLOCED];
|
|
Atom *props;
|
|
Atom p[PREALLOCED];
|
|
int i = 0;
|
|
int j = 0;
|
|
|
|
/* Allocate */
|
|
targets = (Atom *) XtStackAlloc(count * sizeof(Atom), t);
|
|
cbs = (XtSelectionCallbackProc *)
|
|
XtStackAlloc(count * sizeof(XtSelectionCallbackProc), c);
|
|
closures = (XtPointer *) XtStackAlloc(count * sizeof(XtPointer), cs);
|
|
incrs = (Boolean *) XtStackAlloc(count * sizeof(Boolean), ins);
|
|
props = (Atom *) XtStackAlloc(count * sizeof(Atom), p);
|
|
|
|
/* Copy */
|
|
for(i = 0; i < queueInfo->count; i++) {
|
|
if (req[i]->selection == selection) {
|
|
targets[j] = req[i]->target;
|
|
cbs[j] = req[i]->callback;
|
|
closures[j] = req[i]->closure;
|
|
incrs[j] = req[i]->incremental;
|
|
props[j] = req[i]->param;
|
|
j++;
|
|
}
|
|
}
|
|
|
|
/* Make the request */
|
|
GetSelectionValues(widget, selection, targets, count,
|
|
cbs, count, closures, time, incrs, props);
|
|
|
|
/* Free */
|
|
XtStackFree((XtPointer) targets, t);
|
|
XtStackFree((XtPointer) cbs, c);
|
|
XtStackFree((XtPointer) closures, cs);
|
|
XtStackFree((XtPointer) incrs, ins);
|
|
XtStackFree((XtPointer) props, p);
|
|
}
|
|
}
|
|
}
|
|
|
|
CleanupRequest(dpy, queueInfo, selection);
|
|
UNLOCK_PROCESS;
|
|
}
|
|
|
|
extern void XtCancelSelectionRequest(
|
|
Widget widget,
|
|
Atom selection)
|
|
{
|
|
QueuedRequestInfo queueInfo;
|
|
Window window = XtWindow(widget);
|
|
Display *dpy = XtDisplay(widget);
|
|
|
|
LOCK_PROCESS;
|
|
if (multipleContext == 0) multipleContext = XUniqueContext();
|
|
|
|
queueInfo = NULL;
|
|
(void) XFindContext(dpy, window, multipleContext, (XPointer*) &queueInfo);
|
|
/* If there is one, then cancel it */
|
|
if (queueInfo != 0)
|
|
CleanupRequest(dpy, queueInfo, selection);
|
|
UNLOCK_PROCESS;
|
|
}
|
|
|
|
/* Parameter utilities */
|
|
|
|
/* Parameters on a selection request */
|
|
/* Places data on allocated parameter atom, then records the
|
|
parameter atom data for use in the next call to one of
|
|
the XtGetSelectionValue functions. */
|
|
void XtSetSelectionParameters(
|
|
Widget requestor,
|
|
Atom selection,
|
|
Atom type,
|
|
XtPointer value,
|
|
unsigned long length,
|
|
int format)
|
|
{
|
|
Display *dpy = XtDisplay(requestor);
|
|
Window window = XtWindow(requestor);
|
|
Atom property = GetParamInfo(requestor, selection);
|
|
|
|
if (property == None) {
|
|
property = GetSelectionProperty(dpy);
|
|
AddParamInfo(requestor, selection, property);
|
|
}
|
|
|
|
XChangeProperty(dpy, window, property,
|
|
type, format, PropModeReplace,
|
|
(unsigned char *) value, length);
|
|
}
|
|
|
|
/* Retrieves data passed in a parameter. Data for this is stored
|
|
on the originator's window */
|
|
void XtGetSelectionParameters(
|
|
Widget owner,
|
|
Atom selection,
|
|
XtRequestId request_id,
|
|
Atom* type_return,
|
|
XtPointer* value_return,
|
|
unsigned long* length_return,
|
|
int* format_return)
|
|
{
|
|
Request req;
|
|
Display *dpy = XtDisplay(owner);
|
|
WIDGET_TO_APPCON(owner);
|
|
|
|
*value_return = NULL;
|
|
*length_return = *format_return = 0;
|
|
*type_return = None;
|
|
|
|
LOCK_APP(app);
|
|
|
|
req = GetRequestRecord(owner, selection, request_id);
|
|
|
|
if (req && req->property) {
|
|
unsigned long bytes_after; /* unused */
|
|
StartProtectedSection(dpy, req->requestor);
|
|
XGetWindowProperty(dpy, req->requestor, req->property, 0L, 10000000,
|
|
False, AnyPropertyType, type_return, format_return,
|
|
length_return, &bytes_after,
|
|
(unsigned char**) value_return);
|
|
EndProtectedSection(dpy);
|
|
#ifdef XT_COPY_SELECTION
|
|
if (*value_return) {
|
|
int size = BYTELENGTH(*length_return, *format_return) + 1;
|
|
char *tmp = __XtMalloc((Cardinal) size);
|
|
(void) memmove(tmp, *value_return, size);
|
|
XFree(*value_return);
|
|
*value_return = tmp;
|
|
}
|
|
#endif
|
|
}
|
|
UNLOCK_APP(app);
|
|
}
|
|
|
|
/* Parameters are temporarily stashed in an XContext. A list is used because
|
|
* there may be more than one selection request in progress. The context
|
|
* data is deleted when the list is empty. In the future, the parameter
|
|
* context could be merged with other contexts used during selections.
|
|
*/
|
|
|
|
static void AddParamInfo(
|
|
Widget w,
|
|
Atom selection,
|
|
Atom param_atom)
|
|
{
|
|
int n;
|
|
Param p;
|
|
ParamInfo pinfo;
|
|
|
|
LOCK_PROCESS;
|
|
if (paramPropertyContext == 0)
|
|
paramPropertyContext = XUniqueContext();
|
|
|
|
if (XFindContext(XtDisplay(w), XtWindow(w), paramPropertyContext,
|
|
(XPointer *) &pinfo)) {
|
|
pinfo = (ParamInfo) __XtMalloc(sizeof(ParamInfoRec));
|
|
pinfo->count = 1;
|
|
pinfo->paramlist = XtNew(ParamRec);
|
|
p = pinfo->paramlist;
|
|
(void) XSaveContext(XtDisplay(w), XtWindow(w), paramPropertyContext,
|
|
(char *)pinfo);
|
|
}
|
|
else {
|
|
for (n = pinfo->count, p = pinfo->paramlist; n; n--, p++) {
|
|
if (p->selection == None || p->selection == selection)
|
|
break;
|
|
}
|
|
if (n == 0) {
|
|
pinfo->count++;
|
|
pinfo->paramlist = (Param)
|
|
XtRealloc((char*) pinfo->paramlist,
|
|
pinfo->count * sizeof(ParamRec));
|
|
p = &pinfo->paramlist[pinfo->count - 1];
|
|
(void) XSaveContext(XtDisplay(w), XtWindow(w),
|
|
paramPropertyContext, (char *)pinfo);
|
|
}
|
|
}
|
|
p->selection = selection;
|
|
p->param = param_atom;
|
|
UNLOCK_PROCESS;
|
|
}
|
|
|
|
static void RemoveParamInfo(
|
|
Widget w,
|
|
Atom selection)
|
|
{
|
|
int n;
|
|
Param p;
|
|
ParamInfo pinfo;
|
|
Boolean retain = False;
|
|
|
|
LOCK_PROCESS;
|
|
if (paramPropertyContext
|
|
&& (XFindContext(XtDisplay(w), XtWindow(w), paramPropertyContext,
|
|
(XPointer *) &pinfo) == 0)) {
|
|
|
|
/* Find and invalidate the parameter data. */
|
|
for (n = pinfo->count, p = pinfo->paramlist; n; n--, p++) {
|
|
if (p->selection != None) {
|
|
if (p->selection == selection)
|
|
p->selection = None;
|
|
else
|
|
retain = True;
|
|
}
|
|
}
|
|
/* If there's no valid data remaining, release the context entry. */
|
|
if (! retain) {
|
|
XtFree((char*) pinfo->paramlist);
|
|
XtFree((char*) pinfo);
|
|
XDeleteContext(XtDisplay(w), XtWindow(w), paramPropertyContext);
|
|
}
|
|
}
|
|
UNLOCK_PROCESS;
|
|
}
|
|
|
|
static Atom GetParamInfo(
|
|
Widget w,
|
|
Atom selection)
|
|
{
|
|
int n;
|
|
Param p;
|
|
ParamInfo pinfo;
|
|
Atom atom = None;
|
|
|
|
LOCK_PROCESS;
|
|
if (paramPropertyContext
|
|
&& (XFindContext(XtDisplay(w), XtWindow(w), paramPropertyContext,
|
|
(XPointer *) &pinfo) == 0)) {
|
|
|
|
for (n = pinfo->count, p = pinfo->paramlist; n; n--, p++)
|
|
if (p->selection == selection) {
|
|
atom = p->param;
|
|
break;
|
|
}
|
|
}
|
|
UNLOCK_PROCESS;
|
|
return atom;
|
|
}
|