You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

vncSelection.c 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. /* Copyright 2016-2019 Pierre Ossman for Cendio AB
  2. *
  3. * This is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This software is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this software; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  16. * USA.
  17. */
  18. #ifdef HAVE_DIX_CONFIG_H
  19. #include <dix-config.h>
  20. #endif
  21. #include <X11/Xatom.h>
  22. #include "propertyst.h"
  23. #include "scrnintstr.h"
  24. #include "selection.h"
  25. #include "windowstr.h"
  26. #include "xace.h"
  27. #include "xorg-version.h"
  28. #include "vncExtInit.h"
  29. #include "vncSelection.h"
  30. #include "RFBGlue.h"
  31. #define LOG_NAME "Selection"
  32. #define LOG_ERROR(...) vncLogError(LOG_NAME, __VA_ARGS__)
  33. #define LOG_STATUS(...) vncLogStatus(LOG_NAME, __VA_ARGS__)
  34. #define LOG_INFO(...) vncLogInfo(LOG_NAME, __VA_ARGS__)
  35. #define LOG_DEBUG(...) vncLogDebug(LOG_NAME, __VA_ARGS__)
  36. static Atom xaPRIMARY, xaCLIPBOARD;
  37. static Atom xaTARGETS, xaTIMESTAMP, xaSTRING, xaTEXT, xaUTF8_STRING;
  38. static WindowPtr pWindow;
  39. static Window wid;
  40. static Bool probing;
  41. static Atom activeSelection = None;
  42. static char* cachedData = NULL;
  43. struct VncDataTarget {
  44. ClientPtr client;
  45. Atom selection;
  46. Atom target;
  47. Atom property;
  48. Window requestor;
  49. CARD32 time;
  50. struct VncDataTarget* next;
  51. };
  52. static struct VncDataTarget* vncDataTargetHead;
  53. static int vncCreateSelectionWindow(void);
  54. static int vncOwnSelection(Atom selection);
  55. static int vncConvertSelection(ClientPtr client, Atom selection,
  56. Atom target, Atom property,
  57. Window requestor, CARD32 time,
  58. const char* data);
  59. static int vncProcConvertSelection(ClientPtr client);
  60. static void vncSelectionRequest(Atom selection, Atom target);
  61. static int vncProcSendEvent(ClientPtr client);
  62. static void vncSelectionCallback(CallbackListPtr *callbacks,
  63. void * data, void * args);
  64. static void vncClientStateCallback(CallbackListPtr * l,
  65. void * d, void * p);
  66. static int (*origProcConvertSelection)(ClientPtr);
  67. static int (*origProcSendEvent)(ClientPtr);
  68. void vncSelectionInit(void)
  69. {
  70. xaPRIMARY = MakeAtom("PRIMARY", 7, TRUE);
  71. xaCLIPBOARD = MakeAtom("CLIPBOARD", 9, TRUE);
  72. xaTARGETS = MakeAtom("TARGETS", 7, TRUE);
  73. xaTIMESTAMP = MakeAtom("TIMESTAMP", 9, TRUE);
  74. xaSTRING = MakeAtom("STRING", 6, TRUE);
  75. xaTEXT = MakeAtom("TEXT", 4, TRUE);
  76. xaUTF8_STRING = MakeAtom("UTF8_STRING", 11, TRUE);
  77. /* There are no hooks for when these are internal windows, so
  78. * override the relevant handlers. */
  79. origProcConvertSelection = ProcVector[X_ConvertSelection];
  80. ProcVector[X_ConvertSelection] = vncProcConvertSelection;
  81. origProcSendEvent = ProcVector[X_SendEvent];
  82. ProcVector[X_SendEvent] = vncProcSendEvent;
  83. if (!AddCallback(&SelectionCallback, vncSelectionCallback, 0))
  84. FatalError("Add VNC SelectionCallback failed\n");
  85. if (!AddCallback(&ClientStateCallback, vncClientStateCallback, 0))
  86. FatalError("Add VNC ClientStateCallback failed\n");
  87. }
  88. void vncHandleClipboardRequest(void)
  89. {
  90. if (activeSelection == None) {
  91. LOG_DEBUG("Got request for local clipboard although no clipboard is active");
  92. return;
  93. }
  94. LOG_DEBUG("Got request for local clipboard, re-probing formats");
  95. probing = FALSE;
  96. vncSelectionRequest(activeSelection, xaTARGETS);
  97. }
  98. void vncHandleClipboardAnnounce(int available)
  99. {
  100. /* The data has changed in some way, so whatever is in our cache is
  101. * now stale */
  102. free(cachedData);
  103. cachedData = NULL;
  104. if (available) {
  105. int rc;
  106. LOG_DEBUG("Remote clipboard announced, grabbing local ownership");
  107. if (vncGetSetPrimary()) {
  108. rc = vncOwnSelection(xaPRIMARY);
  109. if (rc != Success)
  110. LOG_ERROR("Could not set PRIMARY selection");
  111. }
  112. rc = vncOwnSelection(xaCLIPBOARD);
  113. if (rc != Success)
  114. LOG_ERROR("Could not set CLIPBOARD selection");
  115. } else {
  116. struct VncDataTarget* next;
  117. if (pWindow == NULL)
  118. return;
  119. LOG_DEBUG("Remote clipboard lost, removing local ownership");
  120. DeleteWindowFromAnySelections(pWindow);
  121. /* Abort any pending transfer */
  122. while (vncDataTargetHead != NULL) {
  123. xEvent event;
  124. event.u.u.type = SelectionNotify;
  125. event.u.selectionNotify.time = vncDataTargetHead->time;
  126. event.u.selectionNotify.requestor = vncDataTargetHead->requestor;
  127. event.u.selectionNotify.selection = vncDataTargetHead->selection;
  128. event.u.selectionNotify.target = vncDataTargetHead->target;
  129. event.u.selectionNotify.property = None;
  130. WriteEventsToClient(vncDataTargetHead->client, 1, &event);
  131. next = vncDataTargetHead->next;
  132. free(vncDataTargetHead);
  133. vncDataTargetHead = next;
  134. }
  135. }
  136. }
  137. void vncHandleClipboardData(const char* data)
  138. {
  139. struct VncDataTarget* next;
  140. LOG_DEBUG("Got remote clipboard data, sending to X11 clients");
  141. free(cachedData);
  142. cachedData = strdup(data);
  143. while (vncDataTargetHead != NULL) {
  144. int rc;
  145. xEvent event;
  146. rc = vncConvertSelection(vncDataTargetHead->client,
  147. vncDataTargetHead->selection,
  148. vncDataTargetHead->target,
  149. vncDataTargetHead->property,
  150. vncDataTargetHead->requestor,
  151. vncDataTargetHead->time,
  152. cachedData);
  153. if (rc != Success) {
  154. event.u.u.type = SelectionNotify;
  155. event.u.selectionNotify.time = vncDataTargetHead->time;
  156. event.u.selectionNotify.requestor = vncDataTargetHead->requestor;
  157. event.u.selectionNotify.selection = vncDataTargetHead->selection;
  158. event.u.selectionNotify.target = vncDataTargetHead->target;
  159. event.u.selectionNotify.property = None;
  160. WriteEventsToClient(vncDataTargetHead->client, 1, &event);
  161. }
  162. next = vncDataTargetHead->next;
  163. free(vncDataTargetHead);
  164. vncDataTargetHead = next;
  165. }
  166. }
  167. static int vncCreateSelectionWindow(void)
  168. {
  169. ScreenPtr pScreen;
  170. int result;
  171. if (pWindow != NULL)
  172. return Success;
  173. pScreen = screenInfo.screens[0];
  174. wid = FakeClientID(0);
  175. pWindow = CreateWindow(wid, pScreen->root,
  176. 0, 0, 100, 100, 0, InputOnly,
  177. 0, NULL, 0, serverClient,
  178. CopyFromParent, &result);
  179. if (!pWindow)
  180. return result;
  181. if (!AddResource(pWindow->drawable.id, RT_WINDOW, pWindow))
  182. return BadAlloc;
  183. LOG_DEBUG("Created selection window");
  184. return Success;
  185. }
  186. static int vncOwnSelection(Atom selection)
  187. {
  188. Selection *pSel;
  189. int rc;
  190. SelectionInfoRec info;
  191. rc = vncCreateSelectionWindow();
  192. if (rc != Success)
  193. return rc;
  194. rc = dixLookupSelection(&pSel, selection, serverClient, DixSetAttrAccess);
  195. if (rc == Success) {
  196. if (pSel->client && (pSel->client != serverClient)) {
  197. xEvent event = {
  198. .u.selectionClear.time = currentTime.milliseconds,
  199. .u.selectionClear.window = pSel->window,
  200. .u.selectionClear.atom = pSel->selection
  201. };
  202. event.u.u.type = SelectionClear;
  203. WriteEventsToClient(pSel->client, 1, &event);
  204. }
  205. } else if (rc == BadMatch) {
  206. pSel = dixAllocateObjectWithPrivates(Selection, PRIVATE_SELECTION);
  207. if (!pSel)
  208. return BadAlloc;
  209. pSel->selection = selection;
  210. rc = XaceHookSelectionAccess(serverClient, &pSel,
  211. DixCreateAccess | DixSetAttrAccess);
  212. if (rc != Success) {
  213. free(pSel);
  214. return rc;
  215. }
  216. pSel->next = CurrentSelections;
  217. CurrentSelections = pSel;
  218. }
  219. else
  220. return rc;
  221. pSel->lastTimeChanged = currentTime;
  222. pSel->window = wid;
  223. pSel->pWin = pWindow;
  224. pSel->client = serverClient;
  225. LOG_DEBUG("Grabbed %s selection", NameForAtom(selection));
  226. info.selection = pSel;
  227. info.client = serverClient;
  228. info.kind = SelectionSetOwner;
  229. CallCallbacks(&SelectionCallback, &info);
  230. return Success;
  231. }
  232. static Bool vncWeAreOwner(Atom selection)
  233. {
  234. Selection *pSel;
  235. int rc;
  236. rc = dixLookupSelection(&pSel, selection, serverClient, DixReadAccess);
  237. if (rc != Success)
  238. return FALSE;
  239. if (pSel->client != serverClient)
  240. return FALSE;
  241. if (pSel->window != wid)
  242. return FALSE;
  243. return TRUE;
  244. }
  245. static void vncMaybeRequestCache(void)
  246. {
  247. /* Telling a client that we have clipboard data will likely mean that
  248. * we can no longer request its clipboard data. This is a problem as
  249. * we might initially own multiple selections and we now just lost
  250. * one, and we still want to be able to service the other one. Solve
  251. * this by requesting the data from the client when we can't affort to
  252. * lose it and cache it. */
  253. /* Already cached? */
  254. if (cachedData != NULL)
  255. return;
  256. if (!vncWeAreOwner(xaCLIPBOARD)) {
  257. if (!vncGetSetPrimary())
  258. return;
  259. if (!vncWeAreOwner(xaPRIMARY))
  260. return;
  261. }
  262. LOG_DEBUG("Requesting clipboard data from client for caching");
  263. vncRequestClipboard();
  264. }
  265. static int vncConvertSelection(ClientPtr client, Atom selection,
  266. Atom target, Atom property,
  267. Window requestor, CARD32 time,
  268. const char* data)
  269. {
  270. Selection *pSel;
  271. WindowPtr pWin;
  272. int rc;
  273. Atom realProperty;
  274. xEvent event;
  275. if (data == NULL) {
  276. LOG_DEBUG("Selection request for %s (type %s)",
  277. NameForAtom(selection), NameForAtom(target));
  278. } else {
  279. LOG_DEBUG("Sending data for selection request for %s (type %s)",
  280. NameForAtom(selection), NameForAtom(target));
  281. }
  282. rc = dixLookupSelection(&pSel, selection, client, DixGetAttrAccess);
  283. if (rc != Success)
  284. return rc;
  285. /* We do not validate the time argument because neither does
  286. * dix/selection.c and some clients (e.g. Qt) relies on this */
  287. rc = dixLookupWindow(&pWin, requestor, client, DixSetAttrAccess);
  288. if (rc != Success)
  289. return rc;
  290. if (property != None)
  291. realProperty = property;
  292. else
  293. realProperty = target;
  294. /* FIXME: MULTIPLE target */
  295. if (target == xaTARGETS) {
  296. Atom targets[] = { xaTARGETS, xaTIMESTAMP,
  297. xaSTRING, xaTEXT, xaUTF8_STRING };
  298. rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
  299. XA_ATOM, 32, PropModeReplace,
  300. sizeof(targets)/sizeof(targets[0]),
  301. targets, TRUE);
  302. if (rc != Success)
  303. return rc;
  304. } else if (target == xaTIMESTAMP) {
  305. rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
  306. XA_INTEGER, 32, PropModeReplace, 1,
  307. &pSel->lastTimeChanged.milliseconds,
  308. TRUE);
  309. if (rc != Success)
  310. return rc;
  311. } else {
  312. if (data == NULL) {
  313. struct VncDataTarget* vdt;
  314. if ((target != xaSTRING) && (target != xaTEXT) &&
  315. (target != xaUTF8_STRING))
  316. return BadMatch;
  317. vdt = calloc(1, sizeof(struct VncDataTarget));
  318. if (vdt == NULL)
  319. return BadAlloc;
  320. vdt->client = client;
  321. vdt->selection = selection;
  322. vdt->target = target;
  323. vdt->property = property;
  324. vdt->requestor = requestor;
  325. vdt->time = time;
  326. vdt->next = vncDataTargetHead;
  327. vncDataTargetHead = vdt;
  328. LOG_DEBUG("Requesting clipboard data from client");
  329. vncRequestClipboard();
  330. return Success;
  331. } else {
  332. if ((target == xaSTRING) || (target == xaTEXT)) {
  333. char* latin1;
  334. latin1 = vncUTF8ToLatin1(data, (size_t)-1);
  335. if (latin1 == NULL)
  336. return BadAlloc;
  337. rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
  338. XA_STRING, 8, PropModeReplace,
  339. strlen(latin1), latin1, TRUE);
  340. free(latin1);
  341. if (rc != Success)
  342. return rc;
  343. } else if (target == xaUTF8_STRING) {
  344. rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
  345. xaUTF8_STRING, 8, PropModeReplace,
  346. strlen(data), data, TRUE);
  347. if (rc != Success)
  348. return rc;
  349. } else {
  350. return BadMatch;
  351. }
  352. }
  353. }
  354. event.u.u.type = SelectionNotify;
  355. event.u.selectionNotify.time = time;
  356. event.u.selectionNotify.requestor = requestor;
  357. event.u.selectionNotify.selection = selection;
  358. event.u.selectionNotify.target = target;
  359. event.u.selectionNotify.property = property;
  360. WriteEventsToClient(client, 1, &event);
  361. return Success;
  362. }
  363. static int vncProcConvertSelection(ClientPtr client)
  364. {
  365. Bool paramsOkay;
  366. WindowPtr pWin;
  367. Selection *pSel;
  368. int rc;
  369. REQUEST(xConvertSelectionReq);
  370. REQUEST_SIZE_MATCH(xConvertSelectionReq);
  371. rc = dixLookupWindow(&pWin, stuff->requestor, client, DixSetAttrAccess);
  372. if (rc != Success)
  373. return rc;
  374. paramsOkay = ValidAtom(stuff->selection) && ValidAtom(stuff->target);
  375. paramsOkay &= (stuff->property == None) || ValidAtom(stuff->property);
  376. if (!paramsOkay) {
  377. client->errorValue = stuff->property;
  378. return BadAtom;
  379. }
  380. /* Do we own this selection? */
  381. rc = dixLookupSelection(&pSel, stuff->selection, client, DixReadAccess);
  382. if (rc == Success && pSel->client == serverClient &&
  383. pSel->window == wid) {
  384. /* cachedData will be NULL for the first request, but can then be
  385. * reused once we've gotten the data once from the client */
  386. rc = vncConvertSelection(client, stuff->selection,
  387. stuff->target, stuff->property,
  388. stuff->requestor, stuff->time,
  389. cachedData);
  390. if (rc != Success) {
  391. xEvent event;
  392. memset(&event, 0, sizeof(xEvent));
  393. event.u.u.type = SelectionNotify;
  394. event.u.selectionNotify.time = stuff->time;
  395. event.u.selectionNotify.requestor = stuff->requestor;
  396. event.u.selectionNotify.selection = stuff->selection;
  397. event.u.selectionNotify.target = stuff->target;
  398. event.u.selectionNotify.property = None;
  399. WriteEventsToClient(client, 1, &event);
  400. }
  401. return Success;
  402. }
  403. return origProcConvertSelection(client);
  404. }
  405. static void vncSelectionRequest(Atom selection, Atom target)
  406. {
  407. Selection *pSel;
  408. xEvent event;
  409. int rc;
  410. rc = vncCreateSelectionWindow();
  411. if (rc != Success)
  412. return;
  413. LOG_DEBUG("Requesting %s for %s selection",
  414. NameForAtom(target), NameForAtom(selection));
  415. rc = dixLookupSelection(&pSel, selection, serverClient, DixGetAttrAccess);
  416. if (rc != Success)
  417. return;
  418. event.u.u.type = SelectionRequest;
  419. event.u.selectionRequest.owner = pSel->window;
  420. event.u.selectionRequest.time = currentTime.milliseconds;
  421. event.u.selectionRequest.requestor = wid;
  422. event.u.selectionRequest.selection = selection;
  423. event.u.selectionRequest.target = target;
  424. event.u.selectionRequest.property = target;
  425. WriteEventsToClient(pSel->client, 1, &event);
  426. }
  427. static Bool vncHasAtom(Atom atom, const Atom list[], size_t size)
  428. {
  429. size_t i;
  430. for (i = 0;i < size;i++) {
  431. if (list[i] == atom)
  432. return TRUE;
  433. }
  434. return FALSE;
  435. }
  436. static void vncHandleSelection(Atom selection, Atom target,
  437. Atom property, Atom requestor,
  438. TimeStamp time)
  439. {
  440. PropertyPtr prop;
  441. int rc;
  442. rc = dixLookupProperty(&prop, pWindow, property,
  443. serverClient, DixReadAccess);
  444. if (rc != Success)
  445. return;
  446. LOG_DEBUG("Selection notification for %s (target %s, property %s, type %s)",
  447. NameForAtom(selection), NameForAtom(target),
  448. NameForAtom(property), NameForAtom(prop->type));
  449. if (target != property)
  450. return;
  451. if (target == xaTARGETS) {
  452. if (prop->format != 32)
  453. return;
  454. if (prop->type != XA_ATOM)
  455. return;
  456. if (probing) {
  457. if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size) ||
  458. vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size)) {
  459. vncMaybeRequestCache();
  460. LOG_DEBUG("Compatible format found, notifying clients");
  461. activeSelection = selection;
  462. vncAnnounceClipboard(TRUE);
  463. }
  464. } else {
  465. if (vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size))
  466. vncSelectionRequest(selection, xaUTF8_STRING);
  467. else if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size))
  468. vncSelectionRequest(selection, xaSTRING);
  469. }
  470. } else if (target == xaSTRING) {
  471. char* filtered;
  472. char* utf8;
  473. if (prop->format != 8)
  474. return;
  475. if (prop->type != xaSTRING)
  476. return;
  477. filtered = vncConvertLF(prop->data, prop->size);
  478. if (filtered == NULL)
  479. return;
  480. utf8 = vncLatin1ToUTF8(filtered, (size_t)-1);
  481. free(filtered);
  482. if (utf8 == NULL)
  483. return;
  484. LOG_DEBUG("Sending clipboard to clients (%d bytes)",
  485. (int)strlen(utf8));
  486. vncSendClipboardData(utf8);
  487. free(utf8);
  488. } else if (target == xaUTF8_STRING) {
  489. char *filtered;
  490. if (prop->format != 8)
  491. return;
  492. if (prop->type != xaUTF8_STRING)
  493. return;
  494. if (!vncIsValidUTF8(prop->data, prop->size)) {
  495. LOG_ERROR("Invalid UTF-8 sequence in clipboard");
  496. return;
  497. }
  498. filtered = vncConvertLF(prop->data, prop->size);
  499. if (filtered == NULL)
  500. return;
  501. LOG_DEBUG("Sending clipboard to clients (%d bytes)",
  502. (int)strlen(filtered));
  503. vncSendClipboardData(filtered);
  504. free(filtered);
  505. }
  506. }
  507. #define SEND_EVENT_BIT 0x80
  508. static int vncProcSendEvent(ClientPtr client)
  509. {
  510. REQUEST(xSendEventReq);
  511. REQUEST_SIZE_MATCH(xSendEventReq);
  512. stuff->event.u.u.type &= ~(SEND_EVENT_BIT);
  513. if (stuff->event.u.u.type == SelectionNotify &&
  514. stuff->event.u.selectionNotify.requestor == wid) {
  515. TimeStamp time;
  516. time = ClientTimeToServerTime(stuff->event.u.selectionNotify.time);
  517. vncHandleSelection(stuff->event.u.selectionNotify.selection,
  518. stuff->event.u.selectionNotify.target,
  519. stuff->event.u.selectionNotify.property,
  520. stuff->event.u.selectionNotify.requestor,
  521. time);
  522. }
  523. return origProcSendEvent(client);
  524. }
  525. static void vncSelectionCallback(CallbackListPtr *callbacks,
  526. void * data, void * args)
  527. {
  528. SelectionInfoRec *info = (SelectionInfoRec *) args;
  529. if (info->selection->selection == activeSelection) {
  530. vncMaybeRequestCache();
  531. LOG_DEBUG("Local clipboard lost, notifying clients");
  532. activeSelection = None;
  533. vncAnnounceClipboard(FALSE);
  534. }
  535. if (info->kind != SelectionSetOwner)
  536. return;
  537. if (info->client == serverClient)
  538. return;
  539. LOG_DEBUG("Selection owner change for %s",
  540. NameForAtom(info->selection->selection));
  541. if ((info->selection->selection != xaPRIMARY) &&
  542. (info->selection->selection != xaCLIPBOARD))
  543. return;
  544. if ((info->selection->selection == xaPRIMARY) &&
  545. !vncGetSendPrimary())
  546. return;
  547. LOG_DEBUG("Got clipboard notification, probing for formats");
  548. probing = TRUE;
  549. vncSelectionRequest(info->selection->selection, xaTARGETS);
  550. }
  551. static void vncClientStateCallback(CallbackListPtr * l,
  552. void * d, void * p)
  553. {
  554. ClientPtr client = ((NewClientInfoRec*)p)->client;
  555. if (client->clientState == ClientStateGone) {
  556. struct VncDataTarget** nextPtr = &vncDataTargetHead;
  557. for (struct VncDataTarget* cur = vncDataTargetHead; cur; cur = *nextPtr) {
  558. if (cur->client == client) {
  559. *nextPtr = cur->next;
  560. free(cur);
  561. continue;
  562. }
  563. nextPtr = &cur->next;
  564. }
  565. }
  566. }