Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /* |
2 | * Copyright © 2002 Keith Packard | |
3 | * | |
4 | * Permission to use, copy, modify, distribute, and sell this software and its | |
5 | * documentation for any purpose is hereby granted without fee, provided that | |
6 | * the above copyright notice appear in all copies and that both that | |
7 | * copyright notice and this permission notice appear in supporting | |
8 | * documentation, and that the name of Keith Packard not be used in | |
9 | * advertising or publicity pertaining to distribution of the software without | |
10 | * specific, written prior permission. Keith Packard makes no | |
11 | * representations about the suitability of this software for any purpose. It | |
12 | * is provided "as is" without express or implied warranty. | |
13 | * | |
14 | * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | |
15 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | |
16 | * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR | |
17 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | |
18 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | |
19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | |
20 | * PERFORMANCE OF THIS SOFTWARE. | |
21 | */ | |
22 | ||
23 | #ifdef HAVE_DIX_CONFIG_H | |
24 | #include <dix-config.h> | |
25 | #endif | |
26 | ||
27 | #include "xfixesint.h" | |
28 | #include "xace.h" | |
29 | ||
30 | static RESTYPE SelectionClientType, SelectionWindowType; | |
31 | static Bool SelectionCallbackRegistered = FALSE; | |
32 | ||
33 | /* | |
34 | * There is a global list of windows selecting for selection events | |
35 | * on every selection. This should be plenty efficient for the | |
36 | * expected usage, if it does become a problem, it should be easily | |
37 | * replaced with a hash table of some kind keyed off the selection atom | |
38 | */ | |
39 | ||
40 | typedef struct _SelectionEvent *SelectionEventPtr; | |
41 | ||
42 | typedef struct _SelectionEvent { | |
43 | SelectionEventPtr next; | |
44 | Atom selection; | |
45 | CARD32 eventMask; | |
46 | ClientPtr pClient; | |
47 | WindowPtr pWindow; | |
48 | XID clientResource; | |
49 | } SelectionEventRec; | |
50 | ||
51 | static SelectionEventPtr selectionEvents; | |
52 | ||
53 | static void | |
54 | XFixesSelectionCallback(CallbackListPtr *callbacks, pointer data, pointer args) | |
55 | { | |
56 | SelectionEventPtr e; | |
57 | SelectionInfoRec *info = (SelectionInfoRec *) args; | |
58 | Selection *selection = info->selection; | |
59 | int subtype; | |
60 | CARD32 eventMask; | |
61 | ||
62 | switch (info->kind) { | |
63 | case SelectionSetOwner: | |
64 | subtype = XFixesSetSelectionOwnerNotify; | |
65 | eventMask = XFixesSetSelectionOwnerNotifyMask; | |
66 | break; | |
67 | case SelectionWindowDestroy: | |
68 | subtype = XFixesSelectionWindowDestroyNotify; | |
69 | eventMask = XFixesSelectionWindowDestroyNotifyMask; | |
70 | break; | |
71 | case SelectionClientClose: | |
72 | subtype = XFixesSelectionClientCloseNotify; | |
73 | eventMask = XFixesSelectionClientCloseNotifyMask; | |
74 | break; | |
75 | default: | |
76 | return; | |
77 | } | |
78 | for (e = selectionEvents; e; e = e->next) { | |
79 | if (e->selection == selection->selection && (e->eventMask & eventMask)) { | |
80 | xXFixesSelectionNotifyEvent ev = { | |
81 | .type = XFixesEventBase + XFixesSelectionNotify, | |
82 | .subtype = subtype, | |
83 | .window = e->pWindow->drawable.id, | |
84 | .owner = (subtype == XFixesSetSelectionOwnerNotify) ? | |
85 | selection->window : 0, | |
86 | .selection = e->selection, | |
87 | .timestamp = currentTime.milliseconds, | |
88 | .selectionTimestamp = selection->lastTimeChanged.milliseconds | |
89 | }; | |
90 | WriteEventsToClient(e->pClient, 1, (xEvent *) &ev); | |
91 | } | |
92 | } | |
93 | } | |
94 | ||
95 | static Bool | |
96 | CheckSelectionCallback(void) | |
97 | { | |
98 | if (selectionEvents) { | |
99 | if (!SelectionCallbackRegistered) { | |
100 | if (!AddCallback(&SelectionCallback, XFixesSelectionCallback, NULL)) | |
101 | return FALSE; | |
102 | SelectionCallbackRegistered = TRUE; | |
103 | } | |
104 | } | |
105 | else { | |
106 | if (SelectionCallbackRegistered) { | |
107 | DeleteCallback(&SelectionCallback, XFixesSelectionCallback, NULL); | |
108 | SelectionCallbackRegistered = FALSE; | |
109 | } | |
110 | } | |
111 | return TRUE; | |
112 | } | |
113 | ||
114 | #define SelectionAllEvents (XFixesSetSelectionOwnerNotifyMask |\ | |
115 | XFixesSelectionWindowDestroyNotifyMask |\ | |
116 | XFixesSelectionClientCloseNotifyMask) | |
117 | ||
118 | static int | |
119 | XFixesSelectSelectionInput(ClientPtr pClient, | |
120 | Atom selection, WindowPtr pWindow, CARD32 eventMask) | |
121 | { | |
122 | pointer val; | |
123 | int rc; | |
124 | SelectionEventPtr *prev, e; | |
125 | ||
126 | rc = XaceHook(XACE_SELECTION_ACCESS, pClient, selection, DixGetAttrAccess); | |
127 | if (rc != Success) | |
128 | return rc; | |
129 | ||
130 | for (prev = &selectionEvents; (e = *prev); prev = &e->next) { | |
131 | if (e->selection == selection && | |
132 | e->pClient == pClient && e->pWindow == pWindow) { | |
133 | break; | |
134 | } | |
135 | } | |
136 | if (!eventMask) { | |
137 | if (e) { | |
138 | FreeResource(e->clientResource, 0); | |
139 | } | |
140 | return Success; | |
141 | } | |
142 | if (!e) { | |
143 | e = (SelectionEventPtr) malloc(sizeof(SelectionEventRec)); | |
144 | if (!e) | |
145 | return BadAlloc; | |
146 | ||
147 | e->next = 0; | |
148 | e->selection = selection; | |
149 | e->pClient = pClient; | |
150 | e->pWindow = pWindow; | |
151 | e->clientResource = FakeClientID(pClient->index); | |
152 | ||
153 | /* | |
154 | * Add a resource hanging from the window to | |
155 | * catch window destroy | |
156 | */ | |
157 | rc = dixLookupResourceByType(&val, pWindow->drawable.id, | |
158 | SelectionWindowType, serverClient, | |
159 | DixGetAttrAccess); | |
160 | if (rc != Success) | |
161 | if (!AddResource(pWindow->drawable.id, SelectionWindowType, | |
162 | (pointer) pWindow)) { | |
163 | free(e); | |
164 | return BadAlloc; | |
165 | } | |
166 | ||
167 | if (!AddResource(e->clientResource, SelectionClientType, (pointer) e)) | |
168 | return BadAlloc; | |
169 | ||
170 | *prev = e; | |
171 | if (!CheckSelectionCallback()) { | |
172 | FreeResource(e->clientResource, 0); | |
173 | return BadAlloc; | |
174 | } | |
175 | } | |
176 | e->eventMask = eventMask; | |
177 | return Success; | |
178 | } | |
179 | ||
180 | int | |
181 | ProcXFixesSelectSelectionInput(ClientPtr client) | |
182 | { | |
183 | REQUEST(xXFixesSelectSelectionInputReq); | |
184 | WindowPtr pWin; | |
185 | int rc; | |
186 | ||
187 | REQUEST_SIZE_MATCH(xXFixesSelectSelectionInputReq); | |
188 | rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); | |
189 | if (rc != Success) | |
190 | return rc; | |
191 | if (stuff->eventMask & ~SelectionAllEvents) { | |
192 | client->errorValue = stuff->eventMask; | |
193 | return BadValue; | |
194 | } | |
195 | return XFixesSelectSelectionInput(client, stuff->selection, | |
196 | pWin, stuff->eventMask); | |
197 | } | |
198 | ||
199 | int | |
200 | SProcXFixesSelectSelectionInput(ClientPtr client) | |
201 | { | |
202 | REQUEST(xXFixesSelectSelectionInputReq); | |
203 | ||
204 | swaps(&stuff->length); | |
205 | swapl(&stuff->window); | |
206 | swapl(&stuff->selection); | |
207 | swapl(&stuff->eventMask); | |
208 | return (*ProcXFixesVector[stuff->xfixesReqType]) (client); | |
209 | } | |
210 | ||
211 | void | |
212 | SXFixesSelectionNotifyEvent(xXFixesSelectionNotifyEvent * from, | |
213 | xXFixesSelectionNotifyEvent * to) | |
214 | { | |
215 | to->type = from->type; | |
216 | cpswaps(from->sequenceNumber, to->sequenceNumber); | |
217 | cpswapl(from->window, to->window); | |
218 | cpswapl(from->owner, to->owner); | |
219 | cpswapl(from->selection, to->selection); | |
220 | cpswapl(from->timestamp, to->timestamp); | |
221 | cpswapl(from->selectionTimestamp, to->selectionTimestamp); | |
222 | } | |
223 | ||
224 | static int | |
225 | SelectionFreeClient(pointer data, XID id) | |
226 | { | |
227 | SelectionEventPtr old = (SelectionEventPtr) data; | |
228 | SelectionEventPtr *prev, e; | |
229 | ||
230 | for (prev = &selectionEvents; (e = *prev); prev = &e->next) { | |
231 | if (e == old) { | |
232 | *prev = e->next; | |
233 | free(e); | |
234 | CheckSelectionCallback(); | |
235 | break; | |
236 | } | |
237 | } | |
238 | return 1; | |
239 | } | |
240 | ||
241 | static int | |
242 | SelectionFreeWindow(pointer data, XID id) | |
243 | { | |
244 | WindowPtr pWindow = (WindowPtr) data; | |
245 | SelectionEventPtr e, next; | |
246 | ||
247 | for (e = selectionEvents; e; e = next) { | |
248 | next = e->next; | |
249 | if (e->pWindow == pWindow) { | |
250 | FreeResource(e->clientResource, 0); | |
251 | } | |
252 | } | |
253 | return 1; | |
254 | } | |
255 | ||
256 | Bool | |
257 | XFixesSelectionInit(void) | |
258 | { | |
259 | SelectionClientType = CreateNewResourceType(SelectionFreeClient, | |
260 | "XFixesSelectionClient"); | |
261 | SelectionWindowType = CreateNewResourceType(SelectionFreeWindow, | |
262 | "XFixesSelectionWindow"); | |
263 | return SelectionClientType && SelectionWindowType; | |
264 | } |