clipboard.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. // SPDX-FileCopyrightText: GSConnect Developers https://github.com/GSConnect
  2. //
  3. // SPDX-License-Identifier: GPL-2.0-or-later
  4. import GObject from 'gi://GObject';
  5. import * as Components from '../components/index.js';
  6. import Plugin from '../plugin.js';
  7. export const Metadata = {
  8. label: _('Clipboard'),
  9. description: _('Share the clipboard content'),
  10. id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.Clipboard',
  11. incomingCapabilities: [
  12. 'kdeconnect.clipboard',
  13. 'kdeconnect.clipboard.connect',
  14. ],
  15. outgoingCapabilities: [
  16. 'kdeconnect.clipboard',
  17. 'kdeconnect.clipboard.connect',
  18. ],
  19. actions: {
  20. clipboardPush: {
  21. label: _('Clipboard Push'),
  22. icon_name: 'edit-paste-symbolic',
  23. parameter_type: null,
  24. incoming: [],
  25. outgoing: ['kdeconnect.clipboard'],
  26. },
  27. clipboardPull: {
  28. label: _('Clipboard Pull'),
  29. icon_name: 'edit-copy-symbolic',
  30. parameter_type: null,
  31. incoming: ['kdeconnect.clipboard'],
  32. outgoing: [],
  33. },
  34. },
  35. };
  36. /**
  37. * Clipboard Plugin
  38. * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/clipboard
  39. */
  40. const ClipboardPlugin = GObject.registerClass({
  41. GTypeName: 'GSConnectClipboardPlugin',
  42. }, class ClipboardPlugin extends Plugin {
  43. _init(device) {
  44. super._init(device, 'clipboard');
  45. this._clipboard = Components.acquire('clipboard');
  46. // Watch local clipboard for changes
  47. this._textChangedId = this._clipboard.connect(
  48. 'notify::text',
  49. this._onLocalClipboardChanged.bind(this)
  50. );
  51. // Buffer content to allow selective sync
  52. this._localBuffer = this._clipboard.text;
  53. this._localTimestamp = 0;
  54. this._remoteBuffer = null;
  55. }
  56. connected() {
  57. super.connected();
  58. // TODO: if we're not auto-syncing local->remote, but we are doing the
  59. // reverse, it's possible older remote content will end up
  60. // overwriting newer local content.
  61. if (!this.settings.get_boolean('send-content'))
  62. return;
  63. if (this._localBuffer === null && this._localTimestamp === 0)
  64. return;
  65. this.device.sendPacket({
  66. type: 'kdeconnect.clipboard.connect',
  67. body: {
  68. content: this._localBuffer,
  69. timestamp: this._localTimestamp,
  70. },
  71. });
  72. }
  73. handlePacket(packet) {
  74. if (!packet.body.hasOwnProperty('content'))
  75. return;
  76. switch (packet.type) {
  77. case 'kdeconnect.clipboard':
  78. this._handleContent(packet);
  79. break;
  80. case 'kdeconnect.clipboard.connect':
  81. this._handleConnectContent(packet);
  82. break;
  83. }
  84. }
  85. _handleContent(packet) {
  86. this._onRemoteClipboardChanged(packet.body.content);
  87. }
  88. _handleConnectContent(packet) {
  89. if (packet.body.hasOwnProperty('timestamp') &&
  90. packet.body.timestamp > this._localTimestamp)
  91. this._onRemoteClipboardChanged(packet.body.content);
  92. }
  93. /*
  94. * Store the local clipboard content and forward it if enabled
  95. */
  96. _onLocalClipboardChanged(clipboard, pspec) {
  97. this._localBuffer = clipboard.text;
  98. this._localTimestamp = Date.now();
  99. if (this.settings.get_boolean('send-content'))
  100. this.clipboardPush();
  101. }
  102. /*
  103. * Store the remote clipboard content and apply it if enabled
  104. */
  105. _onRemoteClipboardChanged(text) {
  106. this._remoteBuffer = text;
  107. if (this.settings.get_boolean('receive-content'))
  108. this.clipboardPull();
  109. }
  110. /**
  111. * Copy to the remote clipboard; called by _onLocalClipboardChanged()
  112. */
  113. clipboardPush() {
  114. // Don't sync if the clipboard is empty or not text
  115. if (this._localTimestamp === 0)
  116. return;
  117. if (this._remoteBuffer !== this._localBuffer) {
  118. this._remoteBuffer = this._localBuffer;
  119. // If the buffer is %null, the clipboard contains non-text content,
  120. // so we neither clear the remote clipboard nor pass the content
  121. if (this._localBuffer !== null) {
  122. this.device.sendPacket({
  123. type: 'kdeconnect.clipboard',
  124. body: {
  125. content: this._localBuffer,
  126. },
  127. });
  128. }
  129. }
  130. }
  131. /**
  132. * Copy from the remote clipboard; called by _onRemoteClipboardChanged()
  133. */
  134. clipboardPull() {
  135. if (this._localBuffer !== this._remoteBuffer) {
  136. this._localBuffer = this._remoteBuffer;
  137. this._localTimestamp = Date.now();
  138. this._clipboard.text = this._remoteBuffer;
  139. }
  140. }
  141. destroy() {
  142. if (this._clipboard && this._textChangedId) {
  143. this._clipboard.disconnect(this._textChangedId);
  144. this._clipboard = Components.release('clipboard');
  145. }
  146. super.destroy();
  147. }
  148. });
  149. export default ClipboardPlugin;