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 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  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. struct VncDataTarget {
  43. ClientPtr client;
  44. Atom selection;
  45. Atom target;
  46. Atom property;
  47. Window requestor;
  48. CARD32 time;
  49. struct VncDataTarget* next;
  50. };
  51. static struct VncDataTarget* vncDataTargetHead;
  52. static int vncCreateSelectionWindow(void);
  53. static int vncOwnSelection(Atom selection);
  54. static int vncConvertSelection(ClientPtr client, Atom selection,
  55. Atom target, Atom property,
  56. Window requestor, CARD32 time,
  57. const char* data);
  58. static int vncProcConvertSelection(ClientPtr client);
  59. static void vncSelectionRequest(Atom selection, Atom target);
  60. static int vncProcSendEvent(ClientPtr client);
  61. static void vncSelectionCallback(CallbackListPtr *callbacks,
  62. void * data, void * args);
  63. static void vncClientStateCallback(CallbackListPtr * l,
  64. void * d, void * p);
  65. static int (*origProcConvertSelection)(ClientPtr);
  66. static int (*origProcSendEvent)(ClientPtr);
  67. void vncSelectionInit(void)
  68. {
  69. xaPRIMARY = MakeAtom("PRIMARY", 7, TRUE);
  70. xaCLIPBOARD = MakeAtom("CLIPBOARD", 9, TRUE);
  71. xaTARGETS = MakeAtom("TARGETS", 7, TRUE);
  72. xaTIMESTAMP = MakeAtom("TIMESTAMP", 9, TRUE);
  73. xaSTRING = MakeAtom("STRING", 6, TRUE);
  74. xaTEXT = MakeAtom("TEXT", 4, TRUE);
  75. xaUTF8_STRING = MakeAtom("UTF8_STRING", 11, TRUE);
  76. /* There are no hooks for when these are internal windows, so
  77. * override the relevant handlers. */
  78. origProcConvertSelection = ProcVector[X_ConvertSelection];
  79. ProcVector[X_ConvertSelection] = vncProcConvertSelection;
  80. origProcSendEvent = ProcVector[X_SendEvent];
  81. ProcVector[X_SendEvent] = vncProcSendEvent;
  82. if (!AddCallback(&SelectionCallback, vncSelectionCallback, 0))
  83. FatalError("Add VNC SelectionCallback failed\n");
  84. if (!AddCallback(&ClientStateCallback, vncClientStateCallback, 0))
  85. FatalError("Add VNC ClientStateCallback failed\n");
  86. }
  87. void vncHandleClipboardRequest(void)
  88. {
  89. if (activeSelection == None) {
  90. LOG_DEBUG("Got request for local clipboard although no clipboard is active");
  91. return;
  92. }
  93. LOG_DEBUG("Got request for local clipboard, re-probing formats");
  94. probing = FALSE;
  95. vncSelectionRequest(activeSelection, xaTARGETS);
  96. }
  97. void vncHandleClipboardAnnounce(int available)
  98. {
  99. if (available) {
  100. int rc;
  101. LOG_DEBUG("Remote clipboard announced, grabbing local ownership");
  102. if (vncGetSetPrimary()) {
  103. rc = vncOwnSelection(xaPRIMARY);
  104. if (rc != Success)
  105. LOG_ERROR("Could not set PRIMARY selection");
  106. }
  107. rc = vncOwnSelection(xaCLIPBOARD);
  108. if (rc != Success)
  109. LOG_ERROR("Could not set CLIPBOARD selection");
  110. } else {
  111. struct VncDataTarget* next;
  112. if (pWindow == NULL)
  113. return;
  114. LOG_DEBUG("Remote clipboard lost, removing local ownership");
  115. DeleteWindowFromAnySelections(pWindow);
  116. /* Abort any pending transfer */
  117. while (vncDataTargetHead != NULL) {
  118. xEvent event;
  119. event.u.u.type = SelectionNotify;
  120. event.u.selectionNotify.time = vncDataTargetHead->time;
  121. event.u.selectionNotify.requestor = vncDataTargetHead->requestor;
  122. event.u.selectionNotify.selection = vncDataTargetHead->selection;
  123. event.u.selectionNotify.target = vncDataTargetHead->target;
  124. event.u.selectionNotify.property = None;
  125. WriteEventsToClient(vncDataTargetHead->client, 1, &event);
  126. next = vncDataTargetHead->next;
  127. free(vncDataTargetHead);
  128. vncDataTargetHead = next;
  129. }
  130. }
  131. }
  132. void vncHandleClipboardData(const char* data)
  133. {
  134. struct VncDataTarget* next;
  135. LOG_DEBUG("Got remote clipboard data, sending to X11 clients");
  136. while (vncDataTargetHead != NULL) {
  137. int rc;
  138. xEvent event;
  139. rc = vncConvertSelection(vncDataTargetHead->client,
  140. vncDataTargetHead->selection,
  141. vncDataTargetHead->target,
  142. vncDataTargetHead->property,
  143. vncDataTargetHead->requestor,
  144. vncDataTargetHead->time,
  145. data);
  146. if (rc != Success) {
  147. event.u.u.type = SelectionNotify;
  148. event.u.selectionNotify.time = vncDataTargetHead->time;
  149. event.u.selectionNotify.requestor = vncDataTargetHead->requestor;
  150. event.u.selectionNotify.selection = vncDataTargetHead->selection;
  151. event.u.selectionNotify.target = vncDataTargetHead->target;
  152. event.u.selectionNotify.property = None;
  153. WriteEventsToClient(vncDataTargetHead->client, 1, &event);
  154. }
  155. next = vncDataTargetHead->next;
  156. free(vncDataTargetHead);
  157. vncDataTargetHead = next;
  158. }
  159. }
  160. static int vncCreateSelectionWindow(void)
  161. {
  162. ScreenPtr pScreen;
  163. int result;
  164. if (pWindow != NULL)
  165. return Success;
  166. pScreen = screenInfo.screens[0];
  167. wid = FakeClientID(0);
  168. pWindow = CreateWindow(wid, pScreen->root,
  169. 0, 0, 100, 100, 0, InputOnly,
  170. 0, NULL, 0, serverClient,
  171. CopyFromParent, &result);
  172. if (!pWindow)
  173. return result;
  174. if (!AddResource(pWindow->drawable.id, RT_WINDOW, pWindow))
  175. return BadAlloc;
  176. LOG_DEBUG("Created selection window");
  177. return Success;
  178. }
  179. static int vncOwnSelection(Atom selection)
  180. {
  181. Selection *pSel;
  182. int rc;
  183. SelectionInfoRec info;
  184. rc = vncCreateSelectionWindow();
  185. if (rc != Success)
  186. return rc;
  187. rc = dixLookupSelection(&pSel, selection, serverClient, DixSetAttrAccess);
  188. if (rc == Success) {
  189. if (pSel->client && (pSel->client != serverClient)) {
  190. xEvent event = {
  191. .u.selectionClear.time = currentTime.milliseconds,
  192. .u.selectionClear.window = pSel->window,
  193. .u.selectionClear.atom = pSel->selection
  194. };
  195. event.u.u.type = SelectionClear;
  196. WriteEventsToClient(pSel->client, 1, &event);
  197. }
  198. } else if (rc == BadMatch) {
  199. pSel = dixAllocateObjectWithPrivates(Selection, PRIVATE_SELECTION);
  200. if (!pSel)
  201. return BadAlloc;
  202. pSel->selection = selection;
  203. rc = XaceHookSelectionAccess(serverClient, &pSel,
  204. DixCreateAccess | DixSetAttrAccess);
  205. if (rc != Success) {
  206. free(pSel);
  207. return rc;
  208. }
  209. pSel->next = CurrentSelections;
  210. CurrentSelections = pSel;
  211. }
  212. else
  213. return rc;
  214. pSel->lastTimeChanged = currentTime;
  215. pSel->window = wid;
  216. pSel->pWin = pWindow;
  217. pSel->client = serverClient;
  218. LOG_DEBUG("Grabbed %s selection", NameForAtom(selection));
  219. info.selection = pSel;
  220. info.client = serverClient;
  221. info.kind = SelectionSetOwner;
  222. CallCallbacks(&SelectionCallback, &info);
  223. return Success;
  224. }
  225. static int vncConvertSelection(ClientPtr client, Atom selection,
  226. Atom target, Atom property,
  227. Window requestor, CARD32 time,
  228. const char* data)
  229. {
  230. Selection *pSel;
  231. WindowPtr pWin;
  232. int rc;
  233. Atom realProperty;
  234. xEvent event;
  235. if (data == NULL) {
  236. LOG_DEBUG("Selection request for %s (type %s)",
  237. NameForAtom(selection), NameForAtom(target));
  238. } else {
  239. LOG_DEBUG("Sending data for selection request for %s (type %s)",
  240. NameForAtom(selection), NameForAtom(target));
  241. }
  242. rc = dixLookupSelection(&pSel, selection, client, DixGetAttrAccess);
  243. if (rc != Success)
  244. return rc;
  245. /* We do not validate the time argument because neither does
  246. * dix/selection.c and some clients (e.g. Qt) relies on this */
  247. rc = dixLookupWindow(&pWin, requestor, client, DixSetAttrAccess);
  248. if (rc != Success)
  249. return rc;
  250. if (property != None)
  251. realProperty = property;
  252. else
  253. realProperty = target;
  254. /* FIXME: MULTIPLE target */
  255. if (target == xaTARGETS) {
  256. Atom targets[] = { xaTARGETS, xaTIMESTAMP,
  257. xaSTRING, xaTEXT, xaUTF8_STRING };
  258. rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
  259. XA_ATOM, 32, PropModeReplace,
  260. sizeof(targets)/sizeof(targets[0]),
  261. targets, TRUE);
  262. if (rc != Success)
  263. return rc;
  264. } else if (target == xaTIMESTAMP) {
  265. rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
  266. XA_INTEGER, 32, PropModeReplace, 1,
  267. &pSel->lastTimeChanged.milliseconds,
  268. TRUE);
  269. if (rc != Success)
  270. return rc;
  271. } else {
  272. if (data == NULL) {
  273. struct VncDataTarget* vdt;
  274. if ((target != xaSTRING) && (target != xaTEXT) &&
  275. (target != xaUTF8_STRING))
  276. return BadMatch;
  277. vdt = calloc(1, sizeof(struct VncDataTarget));
  278. if (vdt == NULL)
  279. return BadAlloc;
  280. vdt->client = client;
  281. vdt->selection = selection;
  282. vdt->target = target;
  283. vdt->property = property;
  284. vdt->requestor = requestor;
  285. vdt->time = time;
  286. vdt->next = vncDataTargetHead;
  287. vncDataTargetHead = vdt;
  288. LOG_DEBUG("Requesting clipboard data from client");
  289. vncRequestClipboard();
  290. return Success;
  291. } else {
  292. if ((target == xaSTRING) || (target == xaTEXT)) {
  293. char* latin1;
  294. latin1 = vncUTF8ToLatin1(data, (size_t)-1);
  295. if (latin1 == NULL)
  296. return BadAlloc;
  297. rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
  298. XA_STRING, 8, PropModeReplace,
  299. strlen(latin1), latin1, TRUE);
  300. vncStrFree(latin1);
  301. if (rc != Success)
  302. return rc;
  303. } else if (target == xaUTF8_STRING) {
  304. rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
  305. xaUTF8_STRING, 8, PropModeReplace,
  306. strlen(data), data, TRUE);
  307. if (rc != Success)
  308. return rc;
  309. } else {
  310. return BadMatch;
  311. }
  312. }
  313. }
  314. event.u.u.type = SelectionNotify;
  315. event.u.selectionNotify.time = time;
  316. event.u.selectionNotify.requestor = requestor;
  317. event.u.selectionNotify.selection = selection;
  318. event.u.selectionNotify.target = target;
  319. event.u.selectionNotify.property = property;
  320. WriteEventsToClient(client, 1, &event);
  321. return Success;
  322. }
  323. static int vncProcConvertSelection(ClientPtr client)
  324. {
  325. Bool paramsOkay;
  326. WindowPtr pWin;
  327. Selection *pSel;
  328. int rc;
  329. REQUEST(xConvertSelectionReq);
  330. REQUEST_SIZE_MATCH(xConvertSelectionReq);
  331. rc = dixLookupWindow(&pWin, stuff->requestor, client, DixSetAttrAccess);
  332. if (rc != Success)
  333. return rc;
  334. paramsOkay = ValidAtom(stuff->selection) && ValidAtom(stuff->target);
  335. paramsOkay &= (stuff->property == None) || ValidAtom(stuff->property);
  336. if (!paramsOkay) {
  337. client->errorValue = stuff->property;
  338. return BadAtom;
  339. }
  340. rc = dixLookupSelection(&pSel, stuff->selection, client, DixReadAccess);
  341. if (rc == Success && pSel->client == serverClient &&
  342. pSel->window == wid) {
  343. rc = vncConvertSelection(client, stuff->selection,
  344. stuff->target, stuff->property,
  345. stuff->requestor, stuff->time, NULL);
  346. if (rc != Success) {
  347. xEvent event;
  348. memset(&event, 0, sizeof(xEvent));
  349. event.u.u.type = SelectionNotify;
  350. event.u.selectionNotify.time = stuff->time;
  351. event.u.selectionNotify.requestor = stuff->requestor;
  352. event.u.selectionNotify.selection = stuff->selection;
  353. event.u.selectionNotify.target = stuff->target;
  354. event.u.selectionNotify.property = None;
  355. WriteEventsToClient(client, 1, &event);
  356. }
  357. return Success;
  358. }
  359. return origProcConvertSelection(client);
  360. }
  361. static void vncSelectionRequest(Atom selection, Atom target)
  362. {
  363. Selection *pSel;
  364. xEvent event;
  365. int rc;
  366. rc = vncCreateSelectionWindow();
  367. if (rc != Success)
  368. return;
  369. LOG_DEBUG("Requesting %s for %s selection",
  370. NameForAtom(target), NameForAtom(selection));
  371. rc = dixLookupSelection(&pSel, selection, serverClient, DixGetAttrAccess);
  372. if (rc != Success)
  373. return;
  374. event.u.u.type = SelectionRequest;
  375. event.u.selectionRequest.owner = pSel->window;
  376. event.u.selectionRequest.time = currentTime.milliseconds;
  377. event.u.selectionRequest.requestor = wid;
  378. event.u.selectionRequest.selection = selection;
  379. event.u.selectionRequest.target = target;
  380. event.u.selectionRequest.property = target;
  381. WriteEventsToClient(pSel->client, 1, &event);
  382. }
  383. static Bool vncHasAtom(Atom atom, const Atom list[], size_t size)
  384. {
  385. size_t i;
  386. for (i = 0;i < size;i++) {
  387. if (list[i] == atom)
  388. return TRUE;
  389. }
  390. return FALSE;
  391. }
  392. static void vncHandleSelection(Atom selection, Atom target,
  393. Atom property, Atom requestor,
  394. TimeStamp time)
  395. {
  396. PropertyPtr prop;
  397. int rc;
  398. rc = dixLookupProperty(&prop, pWindow, property,
  399. serverClient, DixReadAccess);
  400. if (rc != Success)
  401. return;
  402. LOG_DEBUG("Selection notification for %s (target %s, property %s, type %s)",
  403. NameForAtom(selection), NameForAtom(target),
  404. NameForAtom(property), NameForAtom(prop->type));
  405. if (target != property)
  406. return;
  407. if (target == xaTARGETS) {
  408. if (prop->format != 32)
  409. return;
  410. if (prop->type != XA_ATOM)
  411. return;
  412. if (probing) {
  413. if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size) ||
  414. vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size)) {
  415. LOG_DEBUG("Compatible format found, notifying clients");
  416. activeSelection = selection;
  417. vncAnnounceClipboard(TRUE);
  418. }
  419. } else {
  420. if (vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size))
  421. vncSelectionRequest(selection, xaUTF8_STRING);
  422. else if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size))
  423. vncSelectionRequest(selection, xaSTRING);
  424. }
  425. } else if (target == xaSTRING) {
  426. char* filtered;
  427. char* utf8;
  428. if (prop->format != 8)
  429. return;
  430. if (prop->type != xaSTRING)
  431. return;
  432. filtered = vncConvertLF(prop->data, prop->size);
  433. if (filtered == NULL)
  434. return;
  435. utf8 = vncLatin1ToUTF8(filtered, (size_t)-1);
  436. vncStrFree(filtered);
  437. if (utf8 == NULL)
  438. return;
  439. LOG_DEBUG("Sending clipboard to clients (%d bytes)",
  440. (int)strlen(utf8));
  441. vncSendClipboardData(utf8);
  442. vncStrFree(utf8);
  443. } else if (target == xaUTF8_STRING) {
  444. char *filtered;
  445. if (prop->format != 8)
  446. return;
  447. if (prop->type != xaUTF8_STRING)
  448. return;
  449. filtered = vncConvertLF(prop->data, prop->size);
  450. if (filtered == NULL)
  451. return;
  452. LOG_DEBUG("Sending clipboard to clients (%d bytes)",
  453. (int)strlen(filtered));
  454. vncSendClipboardData(filtered);
  455. vncStrFree(filtered);
  456. }
  457. }
  458. #define SEND_EVENT_BIT 0x80
  459. static int vncProcSendEvent(ClientPtr client)
  460. {
  461. REQUEST(xSendEventReq);
  462. REQUEST_SIZE_MATCH(xSendEventReq);
  463. stuff->event.u.u.type &= ~(SEND_EVENT_BIT);
  464. if (stuff->event.u.u.type == SelectionNotify &&
  465. stuff->event.u.selectionNotify.requestor == wid) {
  466. TimeStamp time;
  467. time = ClientTimeToServerTime(stuff->event.u.selectionNotify.time);
  468. vncHandleSelection(stuff->event.u.selectionNotify.selection,
  469. stuff->event.u.selectionNotify.target,
  470. stuff->event.u.selectionNotify.property,
  471. stuff->event.u.selectionNotify.requestor,
  472. time);
  473. }
  474. return origProcSendEvent(client);
  475. }
  476. static void vncSelectionCallback(CallbackListPtr *callbacks,
  477. void * data, void * args)
  478. {
  479. SelectionInfoRec *info = (SelectionInfoRec *) args;
  480. if (info->selection->selection == activeSelection) {
  481. LOG_DEBUG("Local clipboard lost, notifying clients");
  482. activeSelection = None;
  483. vncAnnounceClipboard(FALSE);
  484. }
  485. if (info->kind != SelectionSetOwner)
  486. return;
  487. if (info->client == serverClient)
  488. return;
  489. LOG_DEBUG("Selection owner change for %s",
  490. NameForAtom(info->selection->selection));
  491. if ((info->selection->selection != xaPRIMARY) &&
  492. (info->selection->selection != xaCLIPBOARD))
  493. return;
  494. if ((info->selection->selection == xaPRIMARY) &&
  495. !vncGetSendPrimary())
  496. return;
  497. LOG_DEBUG("Got clipboard notification, probing for formats");
  498. probing = TRUE;
  499. vncSelectionRequest(info->selection->selection, xaTARGETS);
  500. }
  501. static void vncClientStateCallback(CallbackListPtr * l,
  502. void * d, void * p)
  503. {
  504. ClientPtr client = ((NewClientInfoRec*)p)->client;
  505. if (client->clientState == ClientStateGone) {
  506. struct VncDataTarget** nextPtr = &vncDataTargetHead;
  507. for (struct VncDataTarget* cur = vncDataTargetHead; cur; cur = *nextPtr) {
  508. if (cur->client == client) {
  509. *nextPtr = cur->next;
  510. free(cur);
  511. continue;
  512. }
  513. nextPtr = &cur->next;
  514. }
  515. }
  516. }