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 {
|
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() {
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user