clipboard_mac.mm 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "remoting/host/clipboard.h"
  5. #import <Cocoa/Cocoa.h>
  6. #include <stdint.h>
  7. #include <memory>
  8. #include <utility>
  9. #include "base/logging.h"
  10. #include "base/memory/ptr_util.h"
  11. #include "base/strings/string_util.h"
  12. #include "base/strings/sys_string_conversions.h"
  13. #include "base/timer/timer.h"
  14. #include "remoting/base/constants.h"
  15. #include "remoting/base/util.h"
  16. #include "remoting/proto/event.pb.h"
  17. #include "remoting/protocol/clipboard_stub.h"
  18. namespace {
  19. // Clipboard polling interval in milliseconds.
  20. const int64_t kClipboardPollingIntervalMs = 500;
  21. } // namespace
  22. namespace remoting {
  23. class ClipboardMac : public Clipboard {
  24. public:
  25. ClipboardMac();
  26. ClipboardMac(const ClipboardMac&) = delete;
  27. ClipboardMac& operator=(const ClipboardMac&) = delete;
  28. ~ClipboardMac() override;
  29. void Start(
  30. std::unique_ptr<protocol::ClipboardStub> client_clipboard) override;
  31. void InjectClipboardEvent(const protocol::ClipboardEvent& event) override;
  32. private:
  33. void CheckClipboardForChanges();
  34. std::unique_ptr<protocol::ClipboardStub> client_clipboard_;
  35. std::unique_ptr<base::RepeatingTimer> clipboard_polling_timer_;
  36. NSInteger current_change_count_;
  37. };
  38. ClipboardMac::ClipboardMac() : current_change_count_(0) {}
  39. ClipboardMac::~ClipboardMac() {}
  40. void ClipboardMac::Start(
  41. std::unique_ptr<protocol::ClipboardStub> client_clipboard) {
  42. client_clipboard_ = std::move(client_clipboard);
  43. // Synchronize local change-count with the pasteboard's. The change-count is
  44. // used to detect clipboard changes.
  45. current_change_count_ = [[NSPasteboard generalPasteboard] changeCount];
  46. // OS X doesn't provide a clipboard-changed notification. The only way to
  47. // detect clipboard changes is by polling.
  48. clipboard_polling_timer_ = std::make_unique<base::RepeatingTimer>();
  49. clipboard_polling_timer_->Start(
  50. FROM_HERE, base::Milliseconds(kClipboardPollingIntervalMs), this,
  51. &ClipboardMac::CheckClipboardForChanges);
  52. }
  53. void ClipboardMac::InjectClipboardEvent(const protocol::ClipboardEvent& event) {
  54. // Currently we only handle UTF-8 text.
  55. if (event.mime_type().compare(kMimeTypeTextUtf8) != 0)
  56. return;
  57. if (!base::IsStringUTF8AllowingNoncharacters(event.data())) {
  58. LOG(ERROR) << "ClipboardEvent data is not UTF-8 encoded.";
  59. return;
  60. }
  61. // Write text to clipboard.
  62. NSString* text = base::SysUTF8ToNSString(event.data());
  63. NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
  64. [pasteboard clearContents];
  65. [pasteboard writeObjects:@[ text ]];
  66. // Update local change-count to prevent this change from being picked up by
  67. // CheckClipboardForChanges.
  68. current_change_count_ = [[NSPasteboard generalPasteboard] changeCount];
  69. }
  70. void ClipboardMac::CheckClipboardForChanges() {
  71. NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
  72. NSInteger change_count = [pasteboard changeCount];
  73. if (change_count == current_change_count_) {
  74. return;
  75. }
  76. current_change_count_ = change_count;
  77. NSArray* objects =
  78. [pasteboard readObjectsForClasses:@[ [NSString class] ] options:0];
  79. if (![objects count]) {
  80. return;
  81. }
  82. protocol::ClipboardEvent event;
  83. event.set_mime_type(kMimeTypeTextUtf8);
  84. event.set_data(base::SysNSStringToUTF8([objects lastObject]));
  85. client_clipboard_->InjectClipboardEvent(event);
  86. }
  87. std::unique_ptr<Clipboard> Clipboard::Create() {
  88. return base::WrapUnique(new ClipboardMac());
  89. }
  90. } // namespace remoting