mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-19 08:37:54 +01:00

Running clang-format with chromium's style guide. The goal is n-fold: * providing consistency and readability (that's what code guidelines are for) * preventing noise with presubmit checks and git cl format * building on the previous point: making it easier to automatically fix format issues * you name it Please consider using git-hyper-blame to ignore this commit. Bug: webrtc:9340 Change-Id: I694567c4cdf8cee2860958cfe82bfaf25848bb87 Reviewed-on: https://webrtc-review.googlesource.com/81185 Reviewed-by: Patrik Höglund <phoglund@webrtc.org> Cr-Commit-Position: refs/heads/master@{#23660}
277 lines
8.8 KiB
C++
277 lines
8.8 KiB
C++
/*
|
|
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include <memory>
|
|
|
|
#include "modules/desktop_capture/mouse_cursor_monitor.h"
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <X11/extensions/Xfixes.h>
|
|
|
|
#include "modules/desktop_capture/desktop_capture_options.h"
|
|
#include "modules/desktop_capture/desktop_capture_types.h"
|
|
#include "modules/desktop_capture/desktop_frame.h"
|
|
#include "modules/desktop_capture/mouse_cursor.h"
|
|
#include "modules/desktop_capture/x11/x_error_trap.h"
|
|
#include "rtc_base/logging.h"
|
|
|
|
namespace {
|
|
|
|
// WindowCapturer returns window IDs of X11 windows with WM_STATE attribute.
|
|
// These windows may not be immediate children of the root window, because
|
|
// window managers may re-parent them to add decorations. However,
|
|
// XQueryPointer() expects to be passed children of the root. This function
|
|
// searches up the list of the windows to find the root child that corresponds
|
|
// to |window|.
|
|
Window GetTopLevelWindow(Display* display, Window window) {
|
|
while (true) {
|
|
// If the window is in WithdrawnState then look at all of its children.
|
|
::Window root, parent;
|
|
::Window* children;
|
|
unsigned int num_children;
|
|
if (!XQueryTree(display, window, &root, &parent, &children,
|
|
&num_children)) {
|
|
RTC_LOG(LS_ERROR) << "Failed to query for child windows although window"
|
|
<< "does not have a valid WM_STATE.";
|
|
return None;
|
|
}
|
|
if (children)
|
|
XFree(children);
|
|
|
|
if (parent == root)
|
|
break;
|
|
|
|
window = parent;
|
|
}
|
|
|
|
return window;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace webrtc {
|
|
|
|
class MouseCursorMonitorX11 : public MouseCursorMonitor,
|
|
public SharedXDisplay::XEventHandler {
|
|
public:
|
|
MouseCursorMonitorX11(const DesktopCaptureOptions& options, Window window);
|
|
~MouseCursorMonitorX11() override;
|
|
|
|
void Init(Callback* callback, Mode mode) override;
|
|
void Capture() override;
|
|
|
|
private:
|
|
// SharedXDisplay::XEventHandler interface.
|
|
bool HandleXEvent(const XEvent& event) override;
|
|
|
|
Display* display() { return x_display_->display(); }
|
|
|
|
// Captures current cursor shape and stores it in |cursor_shape_|.
|
|
void CaptureCursor();
|
|
|
|
rtc::scoped_refptr<SharedXDisplay> x_display_;
|
|
Callback* callback_;
|
|
Mode mode_;
|
|
Window window_;
|
|
|
|
bool have_xfixes_;
|
|
int xfixes_event_base_;
|
|
int xfixes_error_base_;
|
|
|
|
std::unique_ptr<MouseCursor> cursor_shape_;
|
|
};
|
|
|
|
MouseCursorMonitorX11::MouseCursorMonitorX11(
|
|
const DesktopCaptureOptions& options,
|
|
Window window)
|
|
: x_display_(options.x_display()),
|
|
callback_(NULL),
|
|
mode_(SHAPE_AND_POSITION),
|
|
window_(window),
|
|
have_xfixes_(false),
|
|
xfixes_event_base_(-1),
|
|
xfixes_error_base_(-1) {
|
|
// Set a default initial cursor shape in case XFixes is not present.
|
|
const int kSize = 5;
|
|
std::unique_ptr<DesktopFrame> default_cursor(
|
|
new BasicDesktopFrame(DesktopSize(kSize, kSize)));
|
|
const uint8_t pixels[kSize * kSize] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
|
|
0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff,
|
|
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
uint8_t* ptr = default_cursor->data();
|
|
for (int y = 0; y < kSize; ++y) {
|
|
for (int x = 0; x < kSize; ++x) {
|
|
*ptr++ = pixels[kSize * y + x];
|
|
*ptr++ = pixels[kSize * y + x];
|
|
*ptr++ = pixels[kSize * y + x];
|
|
*ptr++ = 0xff;
|
|
}
|
|
}
|
|
DesktopVector hotspot(2, 2);
|
|
cursor_shape_.reset(new MouseCursor(default_cursor.release(), hotspot));
|
|
}
|
|
|
|
MouseCursorMonitorX11::~MouseCursorMonitorX11() {
|
|
if (have_xfixes_) {
|
|
x_display_->RemoveEventHandler(xfixes_event_base_ + XFixesCursorNotify,
|
|
this);
|
|
}
|
|
}
|
|
|
|
void MouseCursorMonitorX11::Init(Callback* callback, Mode mode) {
|
|
// Init can be called only once per instance of MouseCursorMonitor.
|
|
RTC_DCHECK(!callback_);
|
|
RTC_DCHECK(callback);
|
|
|
|
callback_ = callback;
|
|
mode_ = mode;
|
|
|
|
have_xfixes_ =
|
|
XFixesQueryExtension(display(), &xfixes_event_base_, &xfixes_error_base_);
|
|
|
|
if (have_xfixes_) {
|
|
// Register for changes to the cursor shape.
|
|
XFixesSelectCursorInput(display(), window_, XFixesDisplayCursorNotifyMask);
|
|
x_display_->AddEventHandler(xfixes_event_base_ + XFixesCursorNotify, this);
|
|
|
|
CaptureCursor();
|
|
} else {
|
|
RTC_LOG(LS_INFO) << "X server does not support XFixes.";
|
|
}
|
|
}
|
|
|
|
void MouseCursorMonitorX11::Capture() {
|
|
RTC_DCHECK(callback_);
|
|
|
|
// Process X11 events in case XFixes has sent cursor notification.
|
|
x_display_->ProcessPendingXEvents();
|
|
|
|
// cursor_shape_| is set only if we were notified of a cursor shape change.
|
|
if (cursor_shape_.get())
|
|
callback_->OnMouseCursor(cursor_shape_.release());
|
|
|
|
// Get cursor position if necessary.
|
|
if (mode_ == SHAPE_AND_POSITION) {
|
|
int root_x;
|
|
int root_y;
|
|
int win_x;
|
|
int win_y;
|
|
Window root_window;
|
|
Window child_window;
|
|
unsigned int mask;
|
|
|
|
XErrorTrap error_trap(display());
|
|
Bool result = XQueryPointer(display(), window_, &root_window, &child_window,
|
|
&root_x, &root_y, &win_x, &win_y, &mask);
|
|
CursorState state;
|
|
if (!result || error_trap.GetLastErrorAndDisable() != 0) {
|
|
state = OUTSIDE;
|
|
} else {
|
|
// In screen mode (window_ == root_window) the mouse is always inside.
|
|
// XQueryPointer() sets |child_window| to None if the cursor is outside
|
|
// |window_|.
|
|
state =
|
|
(window_ == root_window || child_window != None) ? INSIDE : OUTSIDE;
|
|
}
|
|
|
|
// As the comments to GetTopLevelWindow() above indicate, in window capture,
|
|
// the cursor position capture happens in |window_|, while the frame catpure
|
|
// happens in |child_window|. These two windows are not alwyas same, as
|
|
// window manager may add some decorations to the |window_|. So translate
|
|
// the coordinate in |window_| to the coordinate space of |child_window|.
|
|
if (window_ != root_window && state == INSIDE) {
|
|
int translated_x, translated_y;
|
|
Window unused;
|
|
if (XTranslateCoordinates(display(), window_, child_window, win_x, win_y,
|
|
&translated_x, &translated_y, &unused)) {
|
|
win_x = translated_x;
|
|
win_y = translated_y;
|
|
}
|
|
}
|
|
|
|
// X11 always starts the coordinate from (0, 0), so we do not need to
|
|
// translate here.
|
|
callback_->OnMouseCursorPosition(DesktopVector(root_x, root_y));
|
|
}
|
|
}
|
|
|
|
bool MouseCursorMonitorX11::HandleXEvent(const XEvent& event) {
|
|
if (have_xfixes_ && event.type == xfixes_event_base_ + XFixesCursorNotify) {
|
|
const XFixesCursorNotifyEvent* cursor_event =
|
|
reinterpret_cast<const XFixesCursorNotifyEvent*>(&event);
|
|
if (cursor_event->subtype == XFixesDisplayCursorNotify) {
|
|
CaptureCursor();
|
|
}
|
|
// Return false, even if the event has been handled, because there might be
|
|
// other listeners for cursor notifications.
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void MouseCursorMonitorX11::CaptureCursor() {
|
|
RTC_DCHECK(have_xfixes_);
|
|
|
|
XFixesCursorImage* img;
|
|
{
|
|
XErrorTrap error_trap(display());
|
|
img = XFixesGetCursorImage(display());
|
|
if (!img || error_trap.GetLastErrorAndDisable() != 0)
|
|
return;
|
|
}
|
|
|
|
std::unique_ptr<DesktopFrame> image(
|
|
new BasicDesktopFrame(DesktopSize(img->width, img->height)));
|
|
|
|
// Xlib stores 32-bit data in longs, even if longs are 64-bits long.
|
|
unsigned long* src = img->pixels;
|
|
uint32_t* dst = reinterpret_cast<uint32_t*>(image->data());
|
|
uint32_t* dst_end = dst + (img->width * img->height);
|
|
while (dst < dst_end) {
|
|
*dst++ = static_cast<uint32_t>(*src++);
|
|
}
|
|
|
|
DesktopVector hotspot(std::min(img->width, img->xhot),
|
|
std::min(img->height, img->yhot));
|
|
|
|
XFree(img);
|
|
|
|
cursor_shape_.reset(new MouseCursor(image.release(), hotspot));
|
|
}
|
|
|
|
// static
|
|
MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
|
|
const DesktopCaptureOptions& options,
|
|
WindowId window) {
|
|
if (!options.x_display())
|
|
return NULL;
|
|
window = GetTopLevelWindow(options.x_display()->display(), window);
|
|
if (window == None)
|
|
return NULL;
|
|
return new MouseCursorMonitorX11(options, window);
|
|
}
|
|
|
|
MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
|
|
const DesktopCaptureOptions& options,
|
|
ScreenId screen) {
|
|
if (!options.x_display())
|
|
return NULL;
|
|
return new MouseCursorMonitorX11(
|
|
options, DefaultRootWindow(options.x_display()->display()));
|
|
}
|
|
|
|
std::unique_ptr<MouseCursorMonitor> MouseCursorMonitor::Create(
|
|
const DesktopCaptureOptions& options) {
|
|
return std::unique_ptr<MouseCursorMonitor>(
|
|
CreateForScreen(options, kFullDesktopScreenId));
|
|
}
|
|
|
|
} // namespace webrtc
|