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