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:
parent
894e6fe9ea
commit
7419a72383
@ -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) {
|
||||
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_ &&
|
||||
e.type == xfixes_event_base_ + XFixesCursorNotify) {
|
||||
XFixesCursorNotifyEvent* cne;
|
||||
cne = reinterpret_cast<XFixesCursorNotifyEvent*>(&e);
|
||||
if (cne->subtype == XFixesDisplayCursorNotify) {
|
||||
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();
|
||||
}
|
||||
} 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() {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user