From 7419a723832e7b74d1fbbd21d95f86a65c0e1da7 Mon Sep 17 00:00:00 2001 From: "sergeyu@chromium.org" Date: Sun, 13 Oct 2013 00:44:09 +0000 Subject: [PATCH] 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 --- .../desktop_capture/screen_capturer_x11.cc | 77 +++++++++++-------- .../desktop_capture/x11/shared_x_display.cc | 45 +++++++++++ .../desktop_capture/x11/shared_x_display.h | 41 ++++++++-- 3 files changed, 123 insertions(+), 40 deletions(-) diff --git a/webrtc/modules/desktop_capture/screen_capturer_x11.cc b/webrtc/modules/desktop_capture/screen_capturer_x11.cc index 780d8cf6d..97bdb658d 100644 --- a/webrtc/modules/desktop_capture/screen_capturer_x11.cc +++ b/webrtc/modules/desktop_capture/screen_capturer_x11.cc @@ -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(&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(&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(&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(&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() { diff --git a/webrtc/modules/desktop_capture/x11/shared_x_display.cc b/webrtc/modules/desktop_capture/x11/shared_x_display.cc index c8f5a3796..78e2be0de 100644 --- a/webrtc/modules/desktop_capture/x11/shared_x_display.cc +++ b/webrtc/modules/desktop_capture/x11/shared_x_display.cc @@ -10,6 +10,8 @@ #include "webrtc/modules/desktop_capture/x11/shared_x_display.h" +#include + #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::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::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 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::iterator it = handlers->second.begin(); + it != handlers->second.end(); ++it) { + if ((*it)->HandleXEvent(e)) + break; + } + } +} + } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/x11/shared_x_display.h b/webrtc/modules/desktop_capture/x11/shared_x_display.h index 93f686702..81b5ef660 100644 --- a/webrtc/modules/desktop_capture/x11/shared_x_display.h +++ b/webrtc/modules/desktop_capture/x11/shared_x_display.h @@ -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 +#include + #include #include @@ -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 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 > EventHandlersMap; + ~SharedXDisplay(); Atomic32 ref_count_; Display* display_; + EventHandlersMap event_handlers_; + DISALLOW_COPY_AND_ASSIGN(SharedXDisplay); };