Upstream version 7.35.139.0
[platform/framework/web/crosswalk.git] / src / third_party / webrtc / modules / desktop_capture / window_capturer_mac.mm
1 /*
2  *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10
11 #include "webrtc/modules/desktop_capture/window_capturer.h"
12
13 #include <assert.h>
14 #include <ApplicationServices/ApplicationServices.h>
15 #include <Cocoa/Cocoa.h>
16 #include <CoreFoundation/CoreFoundation.h>
17
18 #include "webrtc/modules/desktop_capture/desktop_frame.h"
19 #include "webrtc/system_wrappers/interface/logging.h"
20
21 namespace webrtc {
22
23 namespace {
24
25 bool CFStringRefToUtf8(const CFStringRef string, std::string* str_utf8) {
26   assert(string);
27   assert(str_utf8);
28   CFIndex length = CFStringGetLength(string);
29   size_t max_length_utf8 =
30       CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
31   str_utf8->resize(max_length_utf8);
32   CFIndex used_bytes;
33   int result = CFStringGetBytes(
34       string, CFRangeMake(0, length), kCFStringEncodingUTF8, 0, false,
35       reinterpret_cast<UInt8*>(&*str_utf8->begin()), max_length_utf8,
36       &used_bytes);
37   if (result != length) {
38     str_utf8->clear();
39     return false;
40   }
41   str_utf8->resize(used_bytes);
42   return true;
43 }
44
45 class WindowCapturerMac : public WindowCapturer {
46  public:
47   WindowCapturerMac();
48   virtual ~WindowCapturerMac();
49
50   // WindowCapturer interface.
51   virtual bool GetWindowList(WindowList* windows) OVERRIDE;
52   virtual bool SelectWindow(WindowId id) OVERRIDE;
53   virtual bool BringSelectedWindowToFront() OVERRIDE;
54
55   // DesktopCapturer interface.
56   virtual void Start(Callback* callback) OVERRIDE;
57   virtual void Capture(const DesktopRegion& region) OVERRIDE;
58
59  private:
60   Callback* callback_;
61   CGWindowID window_id_;
62
63   DISALLOW_COPY_AND_ASSIGN(WindowCapturerMac);
64 };
65
66 WindowCapturerMac::WindowCapturerMac()
67     : callback_(NULL),
68       window_id_(0) {
69 }
70
71 WindowCapturerMac::~WindowCapturerMac() {
72 }
73
74 bool WindowCapturerMac::GetWindowList(WindowList* windows) {
75   // Only get on screen, non-desktop windows.
76   CFArrayRef window_array = CGWindowListCopyWindowInfo(
77       kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
78       kCGNullWindowID);
79   if (!window_array)
80     return false;
81
82   // Check windows to make sure they have an id, title, and use window layer
83   // other than 0.
84   CFIndex count = CFArrayGetCount(window_array);
85   for (CFIndex i = 0; i < count; ++i) {
86     CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
87         CFArrayGetValueAtIndex(window_array, i));
88     CFStringRef window_title = reinterpret_cast<CFStringRef>(
89         CFDictionaryGetValue(window, kCGWindowName));
90     CFNumberRef window_id = reinterpret_cast<CFNumberRef>(
91         CFDictionaryGetValue(window, kCGWindowNumber));
92     CFNumberRef window_layer = reinterpret_cast<CFNumberRef>(
93         CFDictionaryGetValue(window, kCGWindowLayer));
94     if (window_title && window_id && window_layer) {
95       // Skip windows with layer=0 (menu, dock).
96       int layer;
97       CFNumberGetValue(window_layer, kCFNumberIntType, &layer);
98       if (layer != 0)
99         continue;
100
101       int id;
102       CFNumberGetValue(window_id, kCFNumberIntType, &id);
103       WindowCapturer::Window window;
104       window.id = id;
105       if (!CFStringRefToUtf8(window_title, &(window.title)) ||
106           window.title.empty()) {
107         continue;
108       }
109       windows->push_back(window);
110     }
111   }
112
113   CFRelease(window_array);
114   return true;
115 }
116
117 bool WindowCapturerMac::SelectWindow(WindowId id) {
118   // Request description for the specified window to make sure |id| is valid.
119   CGWindowID ids[1];
120   ids[0] = id;
121   CFArrayRef window_id_array =
122       CFArrayCreate(NULL, reinterpret_cast<const void **>(&ids), 1, NULL);
123   CFArrayRef window_array =
124       CGWindowListCreateDescriptionFromArray(window_id_array);
125   int results_count = window_array ? CFArrayGetCount(window_array) : 0;
126   CFRelease(window_id_array);
127   CFRelease(window_array);
128
129   if (results_count == 0) {
130     // Could not find the window. It might have been closed.
131     return false;
132   }
133
134   window_id_ = id;
135   return true;
136 }
137
138 bool WindowCapturerMac::BringSelectedWindowToFront() {
139   if (!window_id_)
140     return false;
141
142   CGWindowID ids[1];
143   ids[0] = window_id_;
144   CFArrayRef window_id_array =
145       CFArrayCreate(NULL, reinterpret_cast<const void **>(&ids), 1, NULL);
146
147   CFArrayRef window_array =
148       CGWindowListCreateDescriptionFromArray(window_id_array);
149   if (window_array == NULL || 0 == CFArrayGetCount(window_array)) {
150     // Could not find the window. It might have been closed.
151     LOG(LS_INFO) << "Window not found";
152     CFRelease(window_id_array);
153     return false;
154   }
155
156   CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
157       CFArrayGetValueAtIndex(window_array, 0));
158   CFNumberRef pid_ref = reinterpret_cast<CFNumberRef>(
159       CFDictionaryGetValue(window, kCGWindowOwnerPID));
160
161   int pid;
162   CFNumberGetValue(pid_ref, kCFNumberIntType, &pid);
163
164   // TODO(jiayl): this will bring the process main window to the front. We
165   // should find a way to bring only the window to the front.
166   bool result =
167       [[NSRunningApplication runningApplicationWithProcessIdentifier: pid]
168           activateWithOptions: NSApplicationActivateIgnoringOtherApps];
169
170   CFRelease(window_id_array);
171   CFRelease(window_array);
172   return result;
173 }
174
175 void WindowCapturerMac::Start(Callback* callback) {
176   assert(!callback_);
177   assert(callback);
178
179   callback_ = callback;
180 }
181
182 void WindowCapturerMac::Capture(const DesktopRegion& region) {
183   CGImageRef window_image = CGWindowListCreateImage(
184       CGRectNull, kCGWindowListOptionIncludingWindow,
185       window_id_, kCGWindowImageBoundsIgnoreFraming);
186
187   if (!window_image) {
188     callback_->OnCaptureCompleted(NULL);
189     return;
190   }
191
192   int bits_per_pixel = CGImageGetBitsPerPixel(window_image);
193   if (bits_per_pixel != 32) {
194     LOG(LS_ERROR) << "Unsupported window image depth: " << bits_per_pixel;
195     CFRelease(window_image);
196     callback_->OnCaptureCompleted(NULL);
197     return;
198   }
199
200   int width = CGImageGetWidth(window_image);
201   int height = CGImageGetHeight(window_image);
202   CGDataProviderRef provider = CGImageGetDataProvider(window_image);
203   CFDataRef cf_data = CGDataProviderCopyData(provider);
204   DesktopFrame* frame = new BasicDesktopFrame(
205       DesktopSize(width, height));
206
207   int src_stride = CGImageGetBytesPerRow(window_image);
208   const uint8_t* src_data = CFDataGetBytePtr(cf_data);
209   for (int y = 0; y < height; ++y) {
210     memcpy(frame->data() + frame->stride() * y, src_data + src_stride * y,
211            DesktopFrame::kBytesPerPixel * width);
212   }
213
214   CFRelease(cf_data);
215   CFRelease(window_image);
216
217   callback_->OnCaptureCompleted(frame);
218 }
219
220 }  // namespace
221
222 // static
223 WindowCapturer* WindowCapturer::Create(const DesktopCaptureOptions& options) {
224   return new WindowCapturerMac();
225 }
226
227 }  // namespace webrtc