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

View File

@ -10,6 +10,8 @@
#include "webrtc/modules/desktop_capture/x11/shared_x_display.h" #include "webrtc/modules/desktop_capture/x11/shared_x_display.h"
#include <algorithm>
#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/logging.h"
namespace webrtc { namespace webrtc {
@ -20,6 +22,7 @@ SharedXDisplay::SharedXDisplay(Display* display)
} }
SharedXDisplay::~SharedXDisplay() { SharedXDisplay::~SharedXDisplay() {
assert(event_handlers_.empty());
XCloseDisplay(display_); XCloseDisplay(display_);
} }
@ -40,4 +43,46 @@ scoped_refptr<SharedXDisplay> SharedXDisplay::CreateDefault() {
return Create(std::string()); 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 } // namespace webrtc

View File

@ -11,6 +11,9 @@
#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_X11_SHARED_X_DISPLAY_H_ #ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_X11_SHARED_X_DISPLAY_H_
#define 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 <assert.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
@ -24,17 +27,17 @@ namespace webrtc {
// A ref-counted object to store XDisplay connection. // A ref-counted object to store XDisplay connection.
class SharedXDisplay { class SharedXDisplay {
public: 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|. // Takes ownership of |display|.
explicit SharedXDisplay(Display* 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 // Creates a new X11 Display for the |display_name|. NULL is returned if X11
// connection failed. Equivalent to CreateDefault() when |display_name| is // connection failed. Equivalent to CreateDefault() when |display_name| is
// empty. // empty.
@ -44,12 +47,34 @@ class SharedXDisplay {
// DISPLAY). NULL is returned if X11 connection failed. // DISPLAY). NULL is returned if X11 connection failed.
static scoped_refptr<SharedXDisplay> CreateDefault(); 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: private:
typedef std::map<int, std::vector<XEventHandler*> > EventHandlersMap;
~SharedXDisplay(); ~SharedXDisplay();
Atomic32 ref_count_; Atomic32 ref_count_;
Display* display_; Display* display_;
EventHandlersMap event_handlers_;
DISALLOW_COPY_AND_ASSIGN(SharedXDisplay); DISALLOW_COPY_AND_ASSIGN(SharedXDisplay);
}; };