| 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 | } |