Add event handling in SharedXDisplay.

SharedXDisplay has to handle X events because the events may belong to
different clients of that class.

R=wez@chromium.org

Review URL: https://webrtc-codereview.appspot.com/2386004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@4953 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
sergeyu@chromium.org 2013-10-13 00:44:09 +00:00
parent 894e6fe9ea
commit 7419a72383
3 changed files with 123 additions and 40 deletions

View File

@ -41,7 +41,8 @@ namespace webrtc {
namespace {
// A class to perform video frame capturing for Linux.
class ScreenCapturerLinux : public ScreenCapturer {
class ScreenCapturerLinux : public ScreenCapturer,
public SharedXDisplay::XEventHandler {
public:
ScreenCapturerLinux();
virtual ~ScreenCapturerLinux();
@ -60,21 +61,17 @@ class ScreenCapturerLinux : public ScreenCapturer {
private:
Display* display() { return options_.x_display()->display(); }
void InitXDamage();
// SharedXDisplay::XEventHandler interface.
virtual bool HandleXEvent(const XEvent& event) OVERRIDE;
// Read and handle all currently-pending XEvents.
// In the DAMAGE case, process the XDamage events and store the resulting
// damage rectangles in the ScreenCapturerHelper.
// In all cases, call ScreenConfigurationChanged() in response to any
// ConfigNotify events.
void ProcessPendingXEvents();
void InitXDamage();
// Capture the cursor image and notify the delegate if it was captured.
void CaptureCursor();
// Capture screen pixels to the current buffer in the queue. In the DAMAGE
// case, the ScreenCapturerHelper already holds the list of invalid rectangles
// from ProcessPendingXEvents(). In the non-DAMAGE case, this captures the
// from HandleXEvent(). In the non-DAMAGE case, this captures the
// whole screen, then calculates some invalid rectangles that include any
// differences between this and the previous capture.
DesktopFrame* CaptureScreen();
@ -149,6 +146,15 @@ ScreenCapturerLinux::ScreenCapturerLinux()
}
ScreenCapturerLinux::~ScreenCapturerLinux() {
options_.x_display()->RemoveEventHandler(ConfigureNotify, this);
if (use_damage_) {
options_.x_display()->RemoveEventHandler(
damage_event_base_ + XDamageNotify, this);
}
if (has_xfixes_) {
options_.x_display()->RemoveEventHandler(
xfixes_event_base_ + XFixesCursorNotify, this);
}
DeinitXlib();
}
@ -169,6 +175,8 @@ bool ScreenCapturerLinux::Init(const DesktopCaptureOptions& options) {
return false;
}
options_.x_display()->AddEventHandler(ConfigureNotify, this);
// Check for XFixes extension. This is required for cursor shape
// notifications, and for our use of XDamage.
if (XFixesQueryExtension(display(), &xfixes_event_base_,
@ -190,6 +198,8 @@ bool ScreenCapturerLinux::Init(const DesktopCaptureOptions& options) {
// Register for changes to the cursor shape.
XFixesSelectCursorInput(display(), root_window_,
XFixesDisplayCursorNotifyMask);
options_.x_display()->AddEventHandler(
xfixes_event_base_ + XFixesCursorNotify, this);
}
if (options_.use_update_notifications()) {
@ -233,6 +243,9 @@ void ScreenCapturerLinux::InitXDamage() {
return;
}
options_.x_display()->AddEventHandler(
damage_event_base_ + XDamageNotify, this);
use_damage_ = true;
LOG(LS_INFO) << "Using XDamage extension.";
}
@ -250,7 +263,7 @@ void ScreenCapturerLinux::Capture(const DesktopRegion& region) {
queue_.MoveToNextFrame();
// Process XEvents for XDamage and cursor shape tracking.
ProcessPendingXEvents();
options_.x_display()->ProcessPendingXEvents();
// ProcessPendingXEvents() may call ScreenConfigurationChanged() which
// reinitializes |x_server_pixel_buffer_|. Check if the pixel buffer is still
@ -297,30 +310,30 @@ void ScreenCapturerLinux::SetMouseShapeObserver(
mouse_shape_observer_ = mouse_shape_observer;
}
void ScreenCapturerLinux::ProcessPendingXEvents() {
// Find the number of events that are outstanding "now." We don't just loop
// on XPending because we want to guarantee this terminates.
int events_to_process = XPending(display());
XEvent e;
for (int i = 0; i < events_to_process; i++) {
XNextEvent(display(), &e);
if (use_damage_ && (e.type == damage_event_base_ + XDamageNotify)) {
XDamageNotifyEvent* event = reinterpret_cast<XDamageNotifyEvent*>(&e);
DCHECK(event->level == XDamageReportNonEmpty);
} else if (e.type == ConfigureNotify) {
ScreenConfigurationChanged();
} else if (has_xfixes_ &&
e.type == xfixes_event_base_ + XFixesCursorNotify) {
XFixesCursorNotifyEvent* cne;
cne = reinterpret_cast<XFixesCursorNotifyEvent*>(&e);
if (cne->subtype == XFixesDisplayCursorNotify) {
CaptureCursor();
}
} else {
LOG(LS_WARNING) << "Got unknown event type: " << e.type;
bool ScreenCapturerLinux::HandleXEvent(const XEvent& event) {
if (use_damage_ && (event.type == damage_event_base_ + XDamageNotify)) {
const XDamageNotifyEvent* damage_event =
reinterpret_cast<const XDamageNotifyEvent*>(&event);
if (damage_event->damage != damage_handle_)
return false;
DCHECK(damage_event->level == XDamageReportNonEmpty);
return true;
} else if (event.type == ConfigureNotify) {
ScreenConfigurationChanged();
return true;
} else if (has_xfixes_ &&
event.type == xfixes_event_base_ + XFixesCursorNotify) {
const XFixesCursorNotifyEvent* cursor_event =
reinterpret_cast<const XFixesCursorNotifyEvent*>(&event);
if (cursor_event->window == root_window_ &&
cursor_event->subtype == XFixesDisplayCursorNotify) {
CaptureCursor();
}
// Always return false for cursor notifications, because there might be
// other listeners for these for the same window.
return false;
}
return false;
}
void ScreenCapturerLinux::CaptureCursor() {

View File

@ -10,6 +10,8 @@
#include "webrtc/modules/desktop_capture/x11/shared_x_display.h"
#include <algorithm>
#include "webrtc/system_wrappers/interface/logging.h"
namespace webrtc {
@ -20,6 +22,7 @@ SharedXDisplay::SharedXDisplay(Display* display)
}
SharedXDisplay::~SharedXDisplay() {
assert(event_handlers_.empty());
XCloseDisplay(display_);
}
@ -40,4 +43,46 @@ scoped_refptr<SharedXDisplay> SharedXDisplay::CreateDefault() {
return Create(std::string());
}
void SharedXDisplay::AddEventHandler(int type, XEventHandler* handler) {
event_handlers_[type].push_back(handler);
}
void SharedXDisplay::RemoveEventHandler(int type, XEventHandler* handler) {
EventHandlersMap::iterator handlers = event_handlers_.find(type);
if (handlers == event_handlers_.end())
return;
std::vector<XEventHandler*>::iterator new_end =
std::remove(handlers->second.begin(), handlers->second.end(), handler);
handlers->second.erase(new_end, handlers->second.end());
// Check if no handlers left for this event.
if (handlers->second.empty())
event_handlers_.erase(handlers);
}
void SharedXDisplay::ProcessPendingXEvents() {
// Hold reference to |this| to prevent it from being destroyed while
// processing events.
scoped_refptr<SharedXDisplay> self(this);
// Find the number of events that are outstanding "now." We don't just loop
// on XPending because we want to guarantee this terminates.
int events_to_process = XPending(display());
XEvent e;
for (int i = 0; i < events_to_process; i++) {
XNextEvent(display(), &e);
EventHandlersMap::iterator handlers = event_handlers_.find(e.type);
if (handlers == event_handlers_.end())
continue;
for (std::vector<XEventHandler*>::iterator it = handlers->second.begin();
it != handlers->second.end(); ++it) {
if ((*it)->HandleXEvent(e))
break;
}
}
}
} // namespace webrtc

View File

@ -11,6 +11,9 @@
#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_X11_SHARED_X_DISPLAY_H_
#define WEBRTC_MODULES_DESKTOP_CAPTURE_X11_SHARED_X_DISPLAY_H_
#include <map>
#include <vector>
#include <assert.h>
#include <X11/Xlib.h>
@ -24,17 +27,17 @@ namespace webrtc {
// A ref-counted object to store XDisplay connection.
class SharedXDisplay {
public:
class XEventHandler {
public:
virtual ~XEventHandler() {}
// Processes XEvent. Returns true if the event has been handled.
virtual bool HandleXEvent(const XEvent& event) = 0;
};
// Takes ownership of |display|.
explicit SharedXDisplay(Display* display);
void AddRef() { ++ref_count_; }
void Release() {
if (--ref_count_ == 0)
delete this;
}
Display* display() { return display_; }
// Creates a new X11 Display for the |display_name|. NULL is returned if X11
// connection failed. Equivalent to CreateDefault() when |display_name| is
// empty.
@ -44,12 +47,34 @@ class SharedXDisplay {
// DISPLAY). NULL is returned if X11 connection failed.
static scoped_refptr<SharedXDisplay> CreateDefault();
void AddRef() { ++ref_count_; }
void Release() {
if (--ref_count_ == 0)
delete this;
}
Display* display() { return display_; }
// Adds a new event |handler| for XEvent's of |type|.
void AddEventHandler(int type, XEventHandler* handler);
// Removes event |handler| added using |AddEventHandler|. Doesn't do anything
// if |handler| is not registered.
void RemoveEventHandler(int type, XEventHandler* handler);
// Processes pending XEvents, calling corresponding event handlers.
void ProcessPendingXEvents();
private:
typedef std::map<int, std::vector<XEventHandler*> > EventHandlersMap;
~SharedXDisplay();
Atomic32 ref_count_;
Display* display_;
EventHandlersMap event_handlers_;
DISALLOW_COPY_AND_ASSIGN(SharedXDisplay);
};