1706f2543Smrg/*
2706f2543Smrg * Copyright © 2002 Keith Packard
3706f2543Smrg *
4706f2543Smrg * Permission to use, copy, modify, distribute, and sell this software and its
5706f2543Smrg * documentation for any purpose is hereby granted without fee, provided that
6706f2543Smrg * the above copyright notice appear in all copies and that both that
7706f2543Smrg * copyright notice and this permission notice appear in supporting
8706f2543Smrg * documentation, and that the name of Keith Packard not be used in
9706f2543Smrg * advertising or publicity pertaining to distribution of the software without
10706f2543Smrg * specific, written prior permission.  Keith Packard makes no
11706f2543Smrg * representations about the suitability of this software for any purpose.  It
12706f2543Smrg * is provided "as is" without express or implied warranty.
13706f2543Smrg *
14706f2543Smrg * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15706f2543Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16706f2543Smrg * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17706f2543Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18706f2543Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19706f2543Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20706f2543Smrg * PERFORMANCE OF THIS SOFTWARE.
21706f2543Smrg */
22706f2543Smrg
23706f2543Smrg#ifdef HAVE_DIX_CONFIG_H
24706f2543Smrg#include <dix-config.h>
25706f2543Smrg#endif
26706f2543Smrg
27706f2543Smrg#include "xfixesint.h"
28706f2543Smrg#include "xace.h"
29706f2543Smrg
30706f2543Smrgstatic RESTYPE		SelectionClientType, SelectionWindowType;
31706f2543Smrgstatic Bool		SelectionCallbackRegistered = FALSE;
32706f2543Smrg
33706f2543Smrg/*
34706f2543Smrg * There is a global list of windows selecting for selection events
35706f2543Smrg * on every selection.  This should be plenty efficient for the
36706f2543Smrg * expected usage, if it does become a problem, it should be easily
37706f2543Smrg * replaced with a hash table of some kind keyed off the selection atom
38706f2543Smrg */
39706f2543Smrg
40706f2543Smrgtypedef struct _SelectionEvent *SelectionEventPtr;
41706f2543Smrg
42706f2543Smrgtypedef struct _SelectionEvent {
43706f2543Smrg    SelectionEventPtr	next;
44706f2543Smrg    Atom		selection;
45706f2543Smrg    CARD32		eventMask;
46706f2543Smrg    ClientPtr		pClient;
47706f2543Smrg    WindowPtr		pWindow;
48706f2543Smrg    XID			clientResource;
49706f2543Smrg} SelectionEventRec;
50706f2543Smrg
51706f2543Smrgstatic SelectionEventPtr	selectionEvents;
52706f2543Smrg
53706f2543Smrgstatic void
54706f2543SmrgXFixesSelectionCallback (CallbackListPtr *callbacks, pointer data, pointer args)
55706f2543Smrg{
56706f2543Smrg    SelectionEventPtr	e;
57706f2543Smrg    SelectionInfoRec	*info = (SelectionInfoRec *) args;
58706f2543Smrg    Selection		*selection = info->selection;
59706f2543Smrg    int			subtype;
60706f2543Smrg    CARD32		eventMask;
61706f2543Smrg
62706f2543Smrg    switch (info->kind) {
63706f2543Smrg    case SelectionSetOwner:
64706f2543Smrg	subtype = XFixesSetSelectionOwnerNotify;
65706f2543Smrg	eventMask = XFixesSetSelectionOwnerNotifyMask;
66706f2543Smrg	break;
67706f2543Smrg    case SelectionWindowDestroy:
68706f2543Smrg	subtype = XFixesSelectionWindowDestroyNotify;
69706f2543Smrg	eventMask = XFixesSelectionWindowDestroyNotifyMask;
70706f2543Smrg	break;
71706f2543Smrg    case SelectionClientClose:
72706f2543Smrg	subtype = XFixesSelectionClientCloseNotify;
73706f2543Smrg	eventMask = XFixesSelectionClientCloseNotifyMask;
74706f2543Smrg	break;
75706f2543Smrg    default:
76706f2543Smrg	return;
77706f2543Smrg    }
78706f2543Smrg    for (e = selectionEvents; e; e = e->next)
79706f2543Smrg    {
80706f2543Smrg	if (e->selection == selection->selection &&
81706f2543Smrg	    (e->eventMask & eventMask))
82706f2543Smrg	{
83706f2543Smrg	    xXFixesSelectionNotifyEvent	ev;
84706f2543Smrg
85706f2543Smrg	    memset(&ev, 0, sizeof(xXFixesSelectionNotifyEvent));
86706f2543Smrg	    ev.type = XFixesEventBase + XFixesSelectionNotify;
87706f2543Smrg	    ev.subtype = subtype;
88706f2543Smrg	    ev.window = e->pWindow->drawable.id;
89706f2543Smrg	    if (subtype == XFixesSetSelectionOwnerNotify)
90706f2543Smrg		ev.owner = selection->window;
91706f2543Smrg	    else
92706f2543Smrg		ev.owner = 0;
93706f2543Smrg	    ev.selection = e->selection;
94706f2543Smrg	    ev.timestamp = currentTime.milliseconds;
95706f2543Smrg	    ev.selectionTimestamp = selection->lastTimeChanged.milliseconds;
96706f2543Smrg	    WriteEventsToClient (e->pClient, 1, (xEvent *) &ev);
97706f2543Smrg	}
98706f2543Smrg    }
99706f2543Smrg}
100706f2543Smrg
101706f2543Smrgstatic Bool
102706f2543SmrgCheckSelectionCallback (void)
103706f2543Smrg{
104706f2543Smrg    if (selectionEvents)
105706f2543Smrg    {
106706f2543Smrg	if (!SelectionCallbackRegistered)
107706f2543Smrg	{
108706f2543Smrg	    if (!AddCallback (&SelectionCallback, XFixesSelectionCallback, NULL))
109706f2543Smrg		return FALSE;
110706f2543Smrg	    SelectionCallbackRegistered = TRUE;
111706f2543Smrg	}
112706f2543Smrg    }
113706f2543Smrg    else
114706f2543Smrg    {
115706f2543Smrg	if (SelectionCallbackRegistered)
116706f2543Smrg	{
117706f2543Smrg	    DeleteCallback (&SelectionCallback, XFixesSelectionCallback, NULL);
118706f2543Smrg	    SelectionCallbackRegistered = FALSE;
119706f2543Smrg	}
120706f2543Smrg    }
121706f2543Smrg    return TRUE;
122706f2543Smrg}
123706f2543Smrg
124706f2543Smrg#define SelectionAllEvents (XFixesSetSelectionOwnerNotifyMask |\
125706f2543Smrg			    XFixesSelectionWindowDestroyNotifyMask |\
126706f2543Smrg			    XFixesSelectionClientCloseNotifyMask)
127706f2543Smrg
128706f2543Smrgstatic int
129706f2543SmrgXFixesSelectSelectionInput (ClientPtr	pClient,
130706f2543Smrg			    Atom	selection,
131706f2543Smrg			    WindowPtr	pWindow,
132706f2543Smrg			    CARD32	eventMask)
133706f2543Smrg{
134706f2543Smrg    pointer val;
135706f2543Smrg    int rc;
136706f2543Smrg    SelectionEventPtr	*prev, e;
137706f2543Smrg
138706f2543Smrg    rc = XaceHook(XACE_SELECTION_ACCESS, pClient, selection, DixGetAttrAccess);
139706f2543Smrg    if (rc != Success)
140706f2543Smrg	return rc;
141706f2543Smrg
142706f2543Smrg    for (prev = &selectionEvents; (e = *prev); prev = &e->next)
143706f2543Smrg    {
144706f2543Smrg	if (e->selection == selection &&
145706f2543Smrg	    e->pClient == pClient &&
146706f2543Smrg	    e->pWindow == pWindow)
147706f2543Smrg	{
148706f2543Smrg	    break;
149706f2543Smrg	}
150706f2543Smrg    }
151706f2543Smrg    if (!eventMask)
152706f2543Smrg    {
153706f2543Smrg	if (e)
154706f2543Smrg	{
155706f2543Smrg	    FreeResource (e->clientResource, 0);
156706f2543Smrg	}
157706f2543Smrg	return Success;
158706f2543Smrg    }
159706f2543Smrg    if (!e)
160706f2543Smrg    {
161706f2543Smrg	e = (SelectionEventPtr) malloc(sizeof (SelectionEventRec));
162706f2543Smrg	if (!e)
163706f2543Smrg	    return BadAlloc;
164706f2543Smrg
165706f2543Smrg	e->next = 0;
166706f2543Smrg	e->selection = selection;
167706f2543Smrg	e->pClient = pClient;
168706f2543Smrg	e->pWindow = pWindow;
169706f2543Smrg	e->clientResource = FakeClientID(pClient->index);
170706f2543Smrg
171706f2543Smrg	/*
172706f2543Smrg	 * Add a resource hanging from the window to
173706f2543Smrg	 * catch window destroy
174706f2543Smrg	 */
175706f2543Smrg	rc = dixLookupResourceByType (&val, pWindow->drawable.id,
176706f2543Smrg				      SelectionWindowType, serverClient,
177706f2543Smrg				      DixGetAttrAccess);
178706f2543Smrg	if (rc != Success)
179706f2543Smrg	    if (!AddResource (pWindow->drawable.id, SelectionWindowType,
180706f2543Smrg			      (pointer) pWindow))
181706f2543Smrg	    {
182706f2543Smrg		free(e);
183706f2543Smrg		return BadAlloc;
184706f2543Smrg	    }
185706f2543Smrg
186706f2543Smrg	if (!AddResource (e->clientResource, SelectionClientType, (pointer) e))
187706f2543Smrg	    return BadAlloc;
188706f2543Smrg
189706f2543Smrg	*prev = e;
190706f2543Smrg	if (!CheckSelectionCallback ())
191706f2543Smrg	{
192706f2543Smrg	    FreeResource (e->clientResource, 0);
193706f2543Smrg	    return BadAlloc;
194706f2543Smrg	}
195706f2543Smrg    }
196706f2543Smrg    e->eventMask = eventMask;
197706f2543Smrg    return Success;
198706f2543Smrg}
199706f2543Smrg
200706f2543Smrgint
201706f2543SmrgProcXFixesSelectSelectionInput (ClientPtr client)
202706f2543Smrg{
203706f2543Smrg    REQUEST (xXFixesSelectSelectionInputReq);
204706f2543Smrg    WindowPtr	pWin;
205706f2543Smrg    int		rc;
206706f2543Smrg
207706f2543Smrg    REQUEST_SIZE_MATCH (xXFixesSelectSelectionInputReq);
208706f2543Smrg    rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
209706f2543Smrg    if (rc != Success)
210706f2543Smrg        return rc;
211706f2543Smrg    if (stuff->eventMask & ~SelectionAllEvents)
212706f2543Smrg    {
213706f2543Smrg	client->errorValue = stuff->eventMask;
214706f2543Smrg	return BadValue;
215706f2543Smrg    }
216706f2543Smrg    return XFixesSelectSelectionInput (client, stuff->selection,
217706f2543Smrg				       pWin, stuff->eventMask);
218706f2543Smrg}
219706f2543Smrg
220706f2543Smrgint
221706f2543SmrgSProcXFixesSelectSelectionInput (ClientPtr client)
222706f2543Smrg{
223706f2543Smrg    register int n;
224706f2543Smrg    REQUEST(xXFixesSelectSelectionInputReq);
225706f2543Smrg
226706f2543Smrg    REQUEST_SIZE_MATCH(xXFixesSelectSelectionInputReq);
227706f2543Smrg    swaps(&stuff->length, n);
228706f2543Smrg    swapl(&stuff->window, n);
229706f2543Smrg    swapl(&stuff->selection, n);
230706f2543Smrg    swapl(&stuff->eventMask, n);
231706f2543Smrg    return (*ProcXFixesVector[stuff->xfixesReqType])(client);
232706f2543Smrg}
233706f2543Smrg
234706f2543Smrgvoid
235706f2543SmrgSXFixesSelectionNotifyEvent (xXFixesSelectionNotifyEvent *from,
236706f2543Smrg			     xXFixesSelectionNotifyEvent *to)
237706f2543Smrg{
238706f2543Smrg    to->type = from->type;
239706f2543Smrg    cpswaps (from->sequenceNumber, to->sequenceNumber);
240706f2543Smrg    cpswapl (from->window, to->window);
241706f2543Smrg    cpswapl (from->owner, to->owner);
242706f2543Smrg    cpswapl (from->selection, to->selection);
243706f2543Smrg    cpswapl (from->timestamp, to->timestamp);
244706f2543Smrg    cpswapl (from->selectionTimestamp, to->selectionTimestamp);
245706f2543Smrg}
246706f2543Smrg
247706f2543Smrgstatic int
248706f2543SmrgSelectionFreeClient (pointer data, XID id)
249706f2543Smrg{
250706f2543Smrg    SelectionEventPtr	old = (SelectionEventPtr) data;
251706f2543Smrg    SelectionEventPtr	*prev, e;
252706f2543Smrg
253706f2543Smrg    for (prev = &selectionEvents; (e = *prev); prev = &e->next)
254706f2543Smrg    {
255706f2543Smrg	if (e == old)
256706f2543Smrg	{
257706f2543Smrg	    *prev = e->next;
258706f2543Smrg	    free(e);
259706f2543Smrg	    CheckSelectionCallback ();
260706f2543Smrg	    break;
261706f2543Smrg	}
262706f2543Smrg    }
263706f2543Smrg    return 1;
264706f2543Smrg}
265706f2543Smrg
266706f2543Smrgstatic int
267706f2543SmrgSelectionFreeWindow (pointer data, XID id)
268706f2543Smrg{
269706f2543Smrg    WindowPtr		pWindow = (WindowPtr) data;
270706f2543Smrg    SelectionEventPtr	e, next;
271706f2543Smrg
272706f2543Smrg    for (e = selectionEvents; e; e = next)
273706f2543Smrg    {
274706f2543Smrg	next = e->next;
275706f2543Smrg	if (e->pWindow == pWindow)
276706f2543Smrg	{
277706f2543Smrg	    FreeResource (e->clientResource, 0);
278706f2543Smrg	}
279706f2543Smrg    }
280706f2543Smrg    return 1;
281706f2543Smrg}
282706f2543Smrg
283706f2543SmrgBool
284706f2543SmrgXFixesSelectionInit (void)
285706f2543Smrg{
286706f2543Smrg    SelectionClientType = CreateNewResourceType(SelectionFreeClient,
287706f2543Smrg						"XFixesSelectionClient");
288706f2543Smrg    SelectionWindowType = CreateNewResourceType(SelectionFreeWindow,
289706f2543Smrg						"XFixesSelectionWindow");
290706f2543Smrg    return SelectionClientType && SelectionWindowType;
291706f2543Smrg}
292