|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T s
Length: 18367 (0x47bf)
Types: TextFile
Names: »sxSelect.c«
└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
└─⟦526ad3590⟧ »EUUGD11/gnu-31mar87/X.V10.R4.tar.Z«
└─⟦2109abc41⟧
└─⟦this⟧ »./X.V10R4/Toolkit/Sx/code/sxSelect.c«
/*
* $Source: /u1/X/Sx/code/RCS/sxSelect.c,v $
* $Header: sxSelect.c,v 1.2 86/12/17 14:46:34 swick Exp $
*/
#ifndef lint
static char *rcsid_sxSelect_c = "$Header: sxSelect.c,v 1.2 86/12/17 14:46:34 swick Exp $";
#endif lint
/*
* sxSelect.c --
*
* This file implements a standard way of dealing with the
* current window selection. It has two major features:
* 1. Selections come in different formats: a selection consists
* of a format (which is a character string) and an arbitrary
* bunch of bytes.
* 2. The selection is accessed by callback procedures. Rather
* than storing the selection in a common place, the module
* that "owns" the selection provides a procedure to call
* whenever the contents of the selection are needed.
*
* Copyright (C) 1986 Regents of the University of California.
* 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. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*/
#ifndef lint
static char rcsid[] = "$Header: sxSelect.c,v 1.2 86/12/17 14:46:34 swick Exp $ SPRITE (Berkeley)";
#endif not lint
/* #include <X/Xlib.h> */
#include "XlibInternal.h"
#include <sys/time.h>
#include "sprite.h"
#include "byte.h"
#include "mem.h"
#include "string.h"
#include "sx.h"
/*
* The information below keeps track of the current selection, if it's
* owned by this client.
*/
static Boolean weveGotIt = FALSE; /* TRUE means either this client owns
* selection; if selFetchProc is NULL,
* then there is no selection. */
static int (*selFetchProc)() = NULL; /* Procedure to call to fetch
* selection, or NULL. */
static void (*selChangeProc)(); /* Procedure to call when selection
* changes. */
static ClientData selClientData; /* Argument to pass to procedures. */
/*
* Each client keeps a tiny transparent window, which is used solely
* for the purpose of signalling between clients. One client signals
* another by unmapping its window; this generates an UnmapWindow
* event to goose the other client into action.
*/
static Window signalWindow = 0; /* Window that this client uses
* to receive signals. 0 means the
* window hasn't been created yet. */
/*
* Three of the cut buffers are used to hold information involved
* in passing the selection between clients:
*
* OWNER_BUFFER: holds 4-byte window id for current selection
* owner's signalWindow.
* REQUEST_BUFFER: holds request for selection information, if
* there is an unprocessed request.
* ANSWER_BUFFER: holds response from current selection owner,
* if there is an unprocessed answer.
*/
#define OWNER_BUFFER 3
#define REQUEST_BUFFER 4
#define ANSWER_BUFFER 5
/*
* Maximum number of bytes of selection that may be requested at once:
*/
#define BYTES_AT_ONCE 1024
/*
* The following two data structures are used to pass selection
* information between clients. Request records are sent to
* the current selection owner, and it responds with Answer
* records.
*/
typedef struct {
char desiredFormat[SX_FORMAT_SIZE]; /* Desired format for selection,
* null-terminated. */
int firstByte; /* First byte of selection to return.*/
int numBytes; /* How many bytes of selection to
* return. A request size of -1
* means that selection ownership is
* switching to the requesting
* client. */
Window signalWindow; /* Where to signal when answer has
* been placed in ANSWER_BUFFER. */
} Request;
typedef struct {
char actualFormat[SX_FORMAT_SIZE]; /* Actual format in which selection is
* being returned (null-terminated). */
int numBytes; /* Number of bytes of selection
* enclosed below. */
char value[BYTES_AT_ONCE]; /* Selection contents. */
} Answer;
/*
* Internal procedures referenced before they're defined:
*/
extern void SelClaim();
extern void SelMakeSignalWindow();
extern void SelSignalProc();
extern int SelTmpHandler();
extern Boolean SelWaitForSignal();
\f
/*
*----------------------------------------------------------------------
*
* Sx_SelectionSet --
*
* Change the selection.
*
* Results:
* None.
*
* Side effects:
* The selection is changed. All that happens is to save the
* name of the procedure to call when the selection is needed.
* FetchProc must be prepared to return part or all of the
* selection anytime it is called (up until the next time changeProc
* is called). FetchProc has the following structure:
*
* int
* fetchProc(clientData, desiredFormat, firstByte, numBytes,
* valuePtr, formatPtr)
* ClientData clientData;
* char *desiredFormat;
* int firstByte;
* int numBytes;
* char *valuePtr;
* char *formatPtr;
* {
* }
*
* In the calls to fetchProc, clientData will be a copy of
* the clientData parameter passed in here. The other parameters,
* plus the return value, all have the same meanings as they do
* for Sx_SelectionGet.
*
* ChangeProc is invoked in the following way:
*
* void
* changeProc(clientData)
* ClientData clientData;
* {
* }
*
* The clientData parameter is identical to the clientData parameter
* passed in here. ChangeProc will be invoked exactly once, the
* next time Sx_SelectionSet or Sx_SelectionClear is invoked.
*
*----------------------------------------------------------------------
*/
void
Sx_SelectionSet(fetchProc, changeProc, clientData)
int (*fetchProc)(); /* Procedure to invoke whenever the
* contents of the selection are needed. */
void (*changeProc)(); /* Procedure to invoke the next time the
* selection is changed. This procedure
* will be invoked exactly once. */
ClientData clientData; /* Arbitrary value to pass to above
* procedures. */
{
if (!weveGotIt) {
SelClaim();
} else {
if (selFetchProc != NULL) {
(*selChangeProc)(selClientData);
}
}
selFetchProc = fetchProc;
selChangeProc = changeProc;
selClientData = clientData;
}
\f
/*
*----------------------------------------------------------------------
*
* Sx_SelectionClear --
*
* Cancel any current selection.
*
* Results:
* None.
*
* Side effects:
* From now until the next call to Sx_SelectionSet, there will
* be no selection.
*
*----------------------------------------------------------------------
*/
void
Sx_SelectionClear()
{
if (!weveGotIt) {
SelClaim();
} else {
if (selFetchProc != NULL) {
(*selChangeProc)(selClientData);
}
selFetchProc = NULL;
}
}
\f
/*
*----------------------------------------------------------------------
*
* Sx_SelectionGet --
*
* Return part or all of the current selection.
*
* Results:
* If there is no selection at present, -1 is returned. Otherwise,
* the return value indicates the number of bytes of selection
* information stored at *valuePtr (if it is less than numBytes,
* then the end of the selection was reached). *ValuePtr gets
* filled in with bytes of the selection, starting with the
* firstByte'th byte in the selection and containing either all
* the remaining bytes in the selection or numBytes bytes,
* whichever is less. The selection will be returned in the
* closest possible form to what was requested with desiredFormat.
* The actual format in which the selection is returned will be
* placed at *formatPtr as an ASCII string containing not more
* than SX_FORMAT_SIZE characters (including the terminating 0).
*
* Side effects:
* None.
*
* Note:
* The formats of selections are completely up to the choice of the
* clients that manage them and will probably evolve over time.
* The one predefined format is "text": in this case, the selection
* bytes consist of ASCII characters (not null-terminated).
*
*----------------------------------------------------------------------
*/
int
Sx_SelectionGet(desiredFormat, firstByte, numBytes, valuePtr, formatPtr)
char *desiredFormat; /* Form in which the selection should
* be returned, if possible. */
int firstByte; /* Index of the first byte of the selection
* that caller is interested in. */
int numBytes; /* Maximum number of bytes that may be
* stored at *valuePtr. */
char *valuePtr; /* Where to store selection information.
* Note: the information stored here
* will not necessarily be null-terminated.
* Depends on format of selection. */
char *formatPtr; /* Actual format of returned selection gets
* stored here, null-terminated. Must be
* at least SX_FORMAT_SIZE bytes here. */
{
Window owner;
char *ownerBuffer;
Request request;
register Answer *ansPtr;
int length, totalCount;
if (weveGotIt) {
if (selFetchProc == NULL) {
return -1;
}
return (*selFetchProc)(selClientData, desiredFormat, firstByte,
numBytes, valuePtr, formatPtr);
}
/*
* Some other client has the selection. Fetch it from them using
* the cut buffers for communication. Get at most BYTES_AT_ONCE
* at a time.
*/
SelMakeSignalWindow();
ownerBuffer = XFetchBuffer(&length, OWNER_BUFFER);
if (length != sizeof(Window)) {
if (ownerBuffer != NULL) {
Mem_Free((Address) ownerBuffer);
return -1;
}
}
owner = *((Window *) ownerBuffer);
Mem_Free((Address) ownerBuffer);
String_NCopy(SX_FORMAT_SIZE, desiredFormat, request.desiredFormat);
request.desiredFormat[SX_FORMAT_SIZE-1] = 0;
totalCount = 0;
while (numBytes > 0) {
request.firstByte = firstByte;
request.numBytes = numBytes;
if (numBytes > BYTES_AT_ONCE) {
request.numBytes = BYTES_AT_ONCE;
}
request.signalWindow = signalWindow;
XStoreBuffer((char *) &request, sizeof(request), REQUEST_BUFFER);
if (!SelUnmapCarefully(owner) || !SelWaitForSignal()) {
return -1;
}
ansPtr = (Answer *) XFetchBuffer(&length, ANSWER_BUFFER);
if (length < (sizeof(Answer) - BYTES_AT_ONCE)) {
if (ansPtr != NULL) {
Mem_Free((Address) ansPtr);
return -1;
}
}
ansPtr->numBytes;
String_NCopy(SX_FORMAT_SIZE, ansPtr->actualFormat, formatPtr);
formatPtr[SX_FORMAT_SIZE-1] = 0;
if (ansPtr->numBytes > 0) {
Byte_Copy(ansPtr->numBytes, ansPtr->value, valuePtr);
valuePtr += ansPtr->numBytes;
firstByte += ansPtr->numBytes;
totalCount += ansPtr->numBytes;
numBytes -= ansPtr->numBytes;
}
Mem_Free((Address) ansPtr);
if (ansPtr->numBytes < request.numBytes) {
if (ansPtr->numBytes < 0) {
return -1;
} else {
break;
}
}
}
return totalCount;
}
\f
/*
*----------------------------------------------------------------------
*
* SelMakeSignalWindow --
*
* Creates the dummy signalling window for this process, if it
* doesn't already exist.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
SelMakeSignalWindow()
{
if (signalWindow != 0) {
return;
}
signalWindow = XCreateTransparency(RootWindow, 0, 0, 1, 1);
if (signalWindow == 0) {
Sx_Panic("Sx selection module couldn't create window for signalling.");
}
(void) Sx_HandlerCreate(signalWindow, UnmapWindow, SelSignalProc,
(ClientData) NULL);
XMapWindow(signalWindow);
}
\f
/*
*----------------------------------------------------------------------
*
* SelSignalProc --
*
* This procedure is invoked by the Sx scheduler whenever someone
* unmaps our signalWindow to request the selection contents.
*
* Results:
* None.
*
* Side effects:
* Stores part of the selection in a cut buffer, and signals the
* requester by unmapping its signal window.
*
*----------------------------------------------------------------------
*/
static void
SelSignalProc()
{
register Request *reqPtr;
Answer answer;
int length;
/*
* Remap the signal window (so we can receive more requests, then
* grab the request and see what we're to do.
*/
XMapWindow(signalWindow);
if (!weveGotIt) {
return;
}
reqPtr = (Request *) XFetchBuffer(&length, REQUEST_BUFFER);
if (length != sizeof(Request)) {
/*
* Bad request; ignore it.
*/
if (length > 0) {
Mem_Free((Address) reqPtr);
}
return;
} else if (reqPtr->numBytes < 0) {
/*
* Someone else is taking the selection. If this client used
* to have it, then call the cleanup procedure.
*/
if (selFetchProc != NULL) {
(*selChangeProc)(selClientData);
}
selFetchProc = NULL;
weveGotIt = FALSE;
Mem_Free((Address) reqPtr);
return;
}
/*
* Someone's requesting part of the selection. If we've got the
* selection, then call the client routine that will return selection
* information, and store the information in a cut buffer (just in
* case the requesting process forgot to terminate the format name,
* do it here).
*/
if (selFetchProc == NULL) {
answer.numBytes = -1;
} else {
reqPtr->desiredFormat[SX_FORMAT_SIZE-1] = 0;
length = reqPtr->numBytes;
if (length > BYTES_AT_ONCE) {
length = BYTES_AT_ONCE;
}
answer.numBytes = (*selFetchProc)(selClientData, reqPtr->desiredFormat,
reqPtr->firstByte, length, answer.value, answer.actualFormat);
answer.actualFormat[SX_FORMAT_SIZE-1] = 0;
}
length = sizeof(Answer) - BYTES_AT_ONCE;
if (answer.numBytes > 0) {
length += answer.numBytes;
}
XStoreBuffer((char *) &answer, length, ANSWER_BUFFER);
(void) SelUnmapCarefully(reqPtr->signalWindow);
Mem_Free((Address) reqPtr);
}
\f
/*
*----------------------------------------------------------------------
*
* SelClaim --
*
* If we aren't the current selection owner, claim the selection
* for us.
*
* Results:
* None.
*
* Side effects:
* The current selection owner is notified that we want the
* selection. "weveGotIt" gets set to TRUE.
*
*----------------------------------------------------------------------
*/
static void
SelClaim()
{
char *ownerBuffer;
int length;
if (weveGotIt) {
return;
}
SelMakeSignalWindow();
/*
* First, notify the previous owner of the selection, if any.
*/
ownerBuffer = XFetchBuffer(&length, OWNER_BUFFER);
if (length == sizeof(Window)) {
Window owner;
Request request;
owner = *((Window *) ownerBuffer);
request.firstByte = -1;
request.numBytes = -1;
XStoreBuffer((char *) &request, sizeof(Request), REQUEST_BUFFER);
(void) SelUnmapCarefully(owner);
}
if (ownerBuffer != NULL) {
Mem_Free((Address) ownerBuffer);
}
/*
* Store us as the current owner of the selection.
*/
XStoreBuffer((char *) &signalWindow, sizeof(Window), OWNER_BUFFER);
weveGotIt = TRUE;
}
\f
/*
*----------------------------------------------------------------------
*
* SelWaitForSignal --
*
* This procedure is called to wait until either a) this client
* gets signalled (by someone unmapping its signalWindow) or
* b) a timeout occurs.
*
* Results:
* If a timeout occurred then FALSE if returned. If all's OK
* then TRUE is returned.
*
* Side effects:
* The signalWindow gets temporarily unmapped, but this procedure
* immediately remaps it.
*
* Note:
* This procedure is a hacked-up copy of the MIT XWindowEvent
* procedure (Copyright 1985 by the Massachusetts Institute of
* Technology).
*
*----------------------------------------------------------------------
*/
static Boolean
SelWaitForSignal()
{
register Display *dpy;
register _QEvent *prev, *qelt;
XEvent event;
extern _QEvent *_qfree;
/*
* Flush output, then see if there's already an event in the
* queue.
*/
dpy = _XlibCurrentDisplay;
_XFlush (dpy);
for (prev = NULL, qelt = dpy->head;
qelt && (qelt->event.window != signalWindow);
prev = qelt, qelt = qelt->next) {
/* Null body. */
}
if (qelt) {
if (prev) {
prev->next = qelt->next;
if (prev->next == NULL) {
dpy->tail = prev;
}
} else {
dpy->head = qelt->next;
if (dpy->head == NULL) {
dpy->tail = NULL;
}
}
qelt->next = _qfree;
_qfree = qelt;
dpy->qlen--;
goto gotSignal;
}
/*
* Nothing there right now. Wait for an event to come in, or
* for several seconds to elapse.
*/
while (1) {
struct timeval timeout;
int mask, numFound;
timeout.tv_sec = 3;
timeout.tv_usec = 0;
mask = 1 << dpy->fd;
numFound = select(dpy->fd + 1, &mask, (int *) 0, (int *) 0, &timeout);
if (numFound <= 0) {
return FALSE;
}
_XRead (dpy, &event, sizeof(XEvent));
if (event.type == X_Error) {
_XError(dpy, (XErrorEvent *) &event);
} else if (event.window == signalWindow) {
goto gotSignal;
} else {
_XEnq(dpy, &event);
}
}
/*
* A signal came in: remap the signalWindow, then return.
*/
gotSignal:
XMapWindow(signalWindow);
return TRUE;
}
\f
/*
*----------------------------------------------------------------------
*
* SelUnmapCarefully --
*
* Unmap a window, ignoring any errors that might occur because
* the window doesn't exist.
*
* Results:
* TRUE is returned if the window exists, FALSE if an error
* occurred while trying to unmap it.
*
* Side effects:
* Window gets unmapped, if it exists.
*
*----------------------------------------------------------------------
*/
static Window unmapWindow; /* For communication between SelUnmapCarefully
* and SelTmpHandler. */
static Boolean unmappedOK;
static int (*savedHandler)();
static Boolean
SelUnmapCarefully(window)
Window window; /* Window to unmap. */
{
savedHandler = _XErrorFunction;
_XErrorFunction = SelTmpHandler;
unmapWindow = window;
unmappedOK = TRUE;
XUnmapWindow(window);
XSync(FALSE);
_XErrorFunction = savedHandler;
return unmappedOK;
}
/* ARGSUSED */
static int
SelTmpHandler(dpyPtr, eventPtr)
Display *dpyPtr; /* Display where error happened. */
XErrorEvent *eventPtr; /* Describes error. */
{
if ((eventPtr->request_code == X_UnmapWindow)
&& (eventPtr->window == unmapWindow)) {
unmappedOK = FALSE;
} else {
(*savedHandler)(dpyPtr, eventPtr);
}
}