// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CHROMEOS_DISPLAY_OUTPUT_CONFIGURATOR_H_ #define CHROMEOS_DISPLAY_OUTPUT_CONFIGURATOR_H_ #include <map> #include <string> #include <vector> #include "base/basictypes.h" #include "base/event_types.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/observer_list.h" #include "base/timer/timer.h" #include "chromeos/chromeos_export.h" #include "third_party/cros_system_api/dbus/service_constants.h" // Forward declarations for Xlib and Xrandr. // This is so unused X definitions don't pollute the namespace. typedef unsigned long XID; typedef XID RROutput; typedef XID RRCrtc; typedef XID RRMode; namespace chromeos { // Used to describe the state of a multi-display configuration. enum OutputState { STATE_INVALID, STATE_HEADLESS, STATE_SINGLE, STATE_DUAL_MIRROR, STATE_DUAL_EXTENDED, }; // Video output types. enum OutputType { OUTPUT_TYPE_NONE = 0, OUTPUT_TYPE_UNKNOWN = 1 << 0, OUTPUT_TYPE_INTERNAL = 1 << 1, OUTPUT_TYPE_VGA = 1 << 2, OUTPUT_TYPE_HDMI = 1 << 3, OUTPUT_TYPE_DVI = 1 << 4, OUTPUT_TYPE_DISPLAYPORT = 1 << 5, OUTPUT_TYPE_NETWORK = 1 << 6, }; // Content protection methods applied on video output. enum OutputProtectionMethod { OUTPUT_PROTECTION_METHOD_NONE = 0, OUTPUT_PROTECTION_METHOD_HDCP = 1 << 0, }; // HDCP protection state. enum HDCPState { HDCP_STATE_UNDESIRED, HDCP_STATE_DESIRED, HDCP_STATE_ENABLED }; // This class interacts directly with the underlying Xrandr API to manipulate // CTRCs and Outputs. class CHROMEOS_EXPORT OutputConfigurator : public base::MessageLoop::Dispatcher, public base::MessagePumpObserver { public: typedef uint64_t OutputProtectionClientId; static const OutputProtectionClientId kInvalidClientId = 0; struct ModeInfo { ModeInfo(); ModeInfo(int width, int height, bool interlaced, float refresh_rate); int width; int height; bool interlaced; float refresh_rate; }; typedef std::map<RRMode, ModeInfo> ModeInfoMap; struct CoordinateTransformation { // Initialized to the identity transformation. CoordinateTransformation(); float x_scale; float x_offset; float y_scale; float y_offset; }; // Information about an output's current state. struct OutputSnapshot { OutputSnapshot(); ~OutputSnapshot(); RROutput output; // CRTC that should be used for this output. Not necessarily the CRTC // that XRandR reports is currently being used. RRCrtc crtc; // Mode currently being used by the output. RRMode current_mode; // "Best" mode supported by the output. RRMode native_mode; // Mode used when displaying the same desktop on multiple outputs. RRMode mirror_mode; // User-selected mode for the output. RRMode selected_mode; // Output's origin on the framebuffer. int x; int y; // Output's physical dimensions. uint64 width_mm; uint64 height_mm; // TODO(kcwu): Remove this. Check type == OUTPUT_TYPE_INTERNAL instead. bool is_internal; bool is_aspect_preserving_scaling; // The type of output. OutputType type; // Map from mode IDs to details about the corresponding modes. ModeInfoMap mode_infos; // XInput device ID or 0 if this output isn't a touchscreen. int touch_device_id; CoordinateTransformation transform; // Display id for this output. int64 display_id; bool has_display_id; // This output's index in the array returned by XRandR. Stable even as // outputs are connected or disconnected. int index; }; class Observer { public: virtual ~Observer() {} // Called after the display mode has been changed. |output| contains the // just-applied configuration. Note that the X server is no longer grabbed // when this method is called, so the actual configuration could've changed // already. virtual void OnDisplayModeChanged( const std::vector<OutputSnapshot>& outputs) {} // Called after a display mode change attempt failed. |failed_new_state| is // the new state which the system failed to enter. virtual void OnDisplayModeChangeFailed(OutputState failed_new_state) {} }; // Interface for classes that make decisions about which output state // should be used. class StateController { public: virtual ~StateController() {} // Called when displays are detected. virtual OutputState GetStateForDisplayIds( const std::vector<int64>& display_ids) const = 0; // Queries the resolution (|width|x|height|) in pixels // to select output mode for the given display id. virtual bool GetResolutionForDisplayId(int64 display_id, int* width, int* height) const = 0; }; // Interface for classes that implement software based mirroring. class SoftwareMirroringController { public: virtual ~SoftwareMirroringController() {} // Called when the hardware mirroring failed. virtual void SetSoftwareMirroring(bool enabled) = 0; }; // Interface for classes that perform actions on behalf of OutputController. class Delegate { public: virtual ~Delegate() {} // Initializes the XRandR extension, saving the base event ID to // |event_base|. virtual void InitXRandRExtension(int* event_base) = 0; // Tells XRandR to update its configuration in response to |event|, an // RRScreenChangeNotify event. virtual void UpdateXRandRConfiguration(const base::NativeEvent& event) = 0; // Grabs the X server and refreshes XRandR-related resources. While // the server is grabbed, other clients are blocked. Must be balanced // by a call to UngrabServer(). virtual void GrabServer() = 0; // Ungrabs the server and frees XRandR-related resources. virtual void UngrabServer() = 0; // Flushes all pending requests and waits for replies. virtual void SyncWithServer() = 0; // Sets the window's background color to |color_argb|. virtual void SetBackgroundColor(uint32 color_argb) = 0; // Enables DPMS and forces it to the "on" state. virtual void ForceDPMSOn() = 0; // Returns information about the current outputs. This method may block for // 60 milliseconds or more. The returned outputs are not fully initialized; // the rest of the work happens in // OutputConfigurator::UpdateCachedOutputs(). virtual std::vector<OutputSnapshot> GetOutputs() = 0; // Adds |mode| to |output|. virtual void AddOutputMode(RROutput output, RRMode mode) = 0; // Calls XRRSetCrtcConfig() with the given options but some of our default // output count and rotation arguments. Returns true on success. virtual bool ConfigureCrtc(RRCrtc crtc, RRMode mode, RROutput output, int x, int y) = 0; // Called to set the frame buffer (underlying XRR "screen") size. Has // a side-effect of disabling all CRTCs. virtual void CreateFrameBuffer( int width, int height, const std::vector<OutputConfigurator::OutputSnapshot>& outputs) = 0; // Configures XInput's Coordinate Transformation Matrix property. // |touch_device_id| the ID of the touchscreen device to configure. // |ctm| contains the desired transformation parameters. The offsets // in it should be normalized so that 1 corresponds to the X or Y axis // size for the corresponding offset. virtual void ConfigureCTM(int touch_device_id, const CoordinateTransformation& ctm) = 0; // Sends a D-Bus message to the power manager telling it that the // machine is or is not projecting. virtual void SendProjectingStateToPowerManager(bool projecting) = 0; // Gets HDCP state of output. virtual bool GetHDCPState(RROutput id, HDCPState* state) = 0; // Sets HDCP state of output. virtual bool SetHDCPState(RROutput id, HDCPState state) = 0; }; // Helper class used by tests. class TestApi { public: TestApi(OutputConfigurator* configurator, int xrandr_event_base) : configurator_(configurator), xrandr_event_base_(xrandr_event_base) {} ~TestApi() {} const std::vector<OutputSnapshot>& cached_outputs() const { return configurator_->cached_outputs_; } // Dispatches an RRScreenChangeNotify event to |configurator_|. void SendScreenChangeEvent(); // Dispatches an RRNotify_OutputChange event to |configurator_|. void SendOutputChangeEvent(RROutput output, RRCrtc crtc, RRMode mode, bool connected); // If |configure_timer_| is started, stops the timer, runs // ConfigureOutputs(), and returns true; returns false otherwise. bool TriggerConfigureTimeout(); private: OutputConfigurator* configurator_; // not owned int xrandr_event_base_; DISALLOW_COPY_AND_ASSIGN(TestApi); }; // Flags that can be passed to SetDisplayPower(). static const int kSetDisplayPowerNoFlags = 0; // Configure displays even if the passed-in state matches |power_state_|. static const int kSetDisplayPowerForceProbe = 1 << 0; // Do not change the state if multiple displays are connected or if the // only connected display is external. static const int kSetDisplayPowerOnlyIfSingleInternalDisplay = 1 << 1; // Gap between screens so cursor at bottom of active display doesn't // partially appear on top of inactive display. Higher numbers guard // against larger cursors, but also waste more memory. // For simplicity, this is hard-coded to avoid the complexity of always // determining the DPI of the screen and rationalizing which screen we // need to use for the DPI calculation. // See crbug.com/130188 for initial discussion. static const int kVerticalGap = 60; // Returns a pointer to the ModeInfo struct in |output| corresponding to // |mode|, or NULL if the struct isn't present. static const ModeInfo* GetModeInfo(const OutputSnapshot& output, RRMode mode); // Returns the mode within |output| that matches the given size with highest // refresh rate. Returns None if no matching output was found. static RRMode FindOutputModeMatchingSize(const OutputSnapshot& output, int width, int height); OutputConfigurator(); virtual ~OutputConfigurator(); OutputState output_state() const { return output_state_; } DisplayPowerState power_state() const { return power_state_; } void set_state_controller(StateController* controller) { state_controller_ = controller; } void set_mirroring_controller(SoftwareMirroringController* controller) { mirroring_controller_ = controller; } // Replaces |delegate_| with |delegate| and sets |configure_display_| to // true. Should be called before Init(). void SetDelegateForTesting(scoped_ptr<Delegate> delegate); // Sets the initial value of |power_state_|. Must be called before Start(). void SetInitialDisplayPower(DisplayPowerState power_state); // Initialization, must be called right after constructor. // |is_panel_fitting_enabled| indicates hardware panel fitting support. void Init(bool is_panel_fitting_enabled); // Does initial configuration of displays during startup. // If |background_color_argb| is non zero and there are multiple displays, // OutputConfigurator sets the background color of X's RootWindow to this // color. void Start(uint32 background_color_argb); // Stop handling display configuration events/requests. void Stop(); // Called when powerd notifies us that some set of displays should be turned // on or off. This requires enabling or disabling the CRTC associated with // the display(s) in question so that the low power state is engaged. // |flags| contains bitwise-or-ed kSetDisplayPower* values. bool SetDisplayPower(DisplayPowerState power_state, int flags); // Force switching the display mode to |new_state|. Returns false if // switching failed (possibly because |new_state| is invalid for the // current set of connected outputs). bool SetDisplayMode(OutputState new_state); // Called when an RRNotify event is received. The implementation is // interested in the cases of RRNotify events which correspond to output // add/remove events. Note that Output add/remove events are sent in response // to our own reconfiguration operations so spurious events are common. // Spurious events will have no effect. virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE; // Overridden from base::MessagePumpObserver: virtual base::EventStatus WillProcessEvent( const base::NativeEvent& event) OVERRIDE; virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE; void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); // Sets all the displays into pre-suspend mode; usually this means // configure them for their resume state. This allows faster resume on // machines where display configuration is slow. void SuspendDisplays(); // Reprobes displays to handle changes made while the system was // suspended. void ResumeDisplays(); const std::map<int, float>& GetMirroredDisplayAreaRatioMap() { return mirrored_display_area_ratio_map_; } // Configure outputs with |kConfigureDelayMs| delay, // so that time-consuming ConfigureOutputs() won't be called multiple times. void ScheduleConfigureOutputs(); // Registers a client for output protection and requests a client id. Returns // 0 if requesting failed. OutputProtectionClientId RegisterOutputProtectionClient(); // Unregisters the client. void UnregisterOutputProtectionClient(OutputProtectionClientId client_id); // Queries link status and protection status. // |link_mask| is the type of connected output links, which is a bitmask of // OutputType values. |protection_mask| is the desired protection methods, // which is a bitmask of the OutputProtectionMethod values. // Returns true on success. bool QueryOutputProtectionStatus( OutputProtectionClientId client_id, int64 display_id, uint32_t* link_mask, uint32_t* protection_mask); // Requests the desired protection methods. // |protection_mask| is the desired protection methods, which is a bitmask // of the OutputProtectionMethod values. // Returns true when the protection request has been made. bool EnableOutputProtection( OutputProtectionClientId client_id, int64 display_id, uint32_t desired_protection_mask); private: // Mapping a display_id to a protection request bitmask. typedef std::map<int64, uint32_t> DisplayProtections; // Mapping a client to its protection request. typedef std::map<OutputProtectionClientId, DisplayProtections> ProtectionRequests; // Updates |cached_outputs_| to contain currently-connected outputs. Calls // |delegate_->GetOutputs()| and then does additional work, like finding the // mirror mode and setting user-preferred modes. Note that the server must be // grabbed via |delegate_->GrabServer()| first. void UpdateCachedOutputs(); // Helper method for UpdateCachedOutputs() that initializes the passed-in // outputs' |mirror_mode| fields by looking for a mode in |internal_output| // and |external_output| having the same resolution. Returns false if a shared // mode wasn't found or created. // // |try_panel_fitting| allows creating a panel-fitting mode for // |internal_output| instead of only searching for a matching mode (note that // it may lead to a crash if |internal_info| is not capable of panel fitting). // // |preserve_aspect| limits the search/creation only to the modes having the // native aspect ratio of |external_output|. bool FindMirrorMode(OutputSnapshot* internal_output, OutputSnapshot* external_output, bool try_panel_fitting, bool preserve_aspect); // Configures outputs. void ConfigureOutputs(); // Notifies observers about an attempted state change. void NotifyObservers(bool success, OutputState attempted_state); // Switches to the state specified in |output_state| and |power_state|. // If the hardware mirroring failed and |mirroring_controller_| is set, // it switches to |STATE_DUAL_EXTENDED| and calls |SetSoftwareMirroring()| // to enable software based mirroring. // On success, updates |output_state_|, |power_state_|, and |cached_outputs_| // and returns true. bool EnterStateOrFallBackToSoftwareMirroring( OutputState output_state, DisplayPowerState power_state); // Switches to the state specified in |output_state| and |power_state|. // On success, updates |output_state_|, |power_state_|, and // |cached_outputs_| and returns true. bool EnterState(OutputState output_state, DisplayPowerState power_state); // Returns the output state that should be used with |cached_outputs_| while // in |power_state|. OutputState ChooseOutputState(DisplayPowerState power_state) const; // Computes the relevant transformation for mirror mode. // |output| is the output on which mirror mode is being applied. // Returns the transformation or identity if computations fail. CoordinateTransformation GetMirrorModeCTM( const OutputConfigurator::OutputSnapshot& output); // Computes the relevant transformation for extended mode. // |output| is the output on which extended mode is being applied. // |width| and |height| are the width and height of the combined framebuffer. // Returns the transformation or identity if computations fail. CoordinateTransformation GetExtendedModeCTM( const OutputConfigurator::OutputSnapshot& output, int framebuffer_width, int frame_buffer_height); // Returns the ratio between mirrored mode area and native mode area: // (mirror_mode_width * mirrow_mode_height) / (native_width * native_height) float GetMirroredDisplayAreaRatio( const OutputConfigurator::OutputSnapshot& output); // Applies output protections according to requests. bool ApplyProtections(const DisplayProtections& requests); StateController* state_controller_; SoftwareMirroringController* mirroring_controller_; scoped_ptr<Delegate> delegate_; // Used to enable modes which rely on panel fitting. bool is_panel_fitting_enabled_; // Key of the map is the touch display's id, and the value of the map is the // touch display's area ratio in mirror mode defined as : // mirror_mode_area / native_mode_area. // This is used for scaling touch event's radius when the touch display is in // mirror mode : // new_touch_radius = sqrt(area_ratio) * old_touch_radius std::map<int, float> mirrored_display_area_ratio_map_; // This is detected by the constructor to determine whether or not we should // be enabled. If we aren't running on ChromeOS, we can't assume that the // Xrandr X11 extension is supported. // If this flag is set to false, any attempts to change the output // configuration to immediately fail without changing the state. bool configure_display_; // The base of the event numbers used to represent XRandr events used in // decoding events regarding output add/remove. int xrandr_event_base_; // The current display state. OutputState output_state_; // The current power state. DisplayPowerState power_state_; // Most-recently-used output configuration. Note that the actual // configuration changes asynchronously. std::vector<OutputSnapshot> cached_outputs_; ObserverList<Observer> observers_; // The timer to delay configuring outputs. See also the comments in // Dispatch(). scoped_ptr<base::OneShotTimer<OutputConfigurator> > configure_timer_; // Id for next output protection client. OutputProtectionClientId next_output_protection_client_id_; // Output protection requests of each client. ProtectionRequests client_protection_requests_; DISALLOW_COPY_AND_ASSIGN(OutputConfigurator); }; typedef std::vector<OutputConfigurator::OutputSnapshot> OutputSnapshotList; } // namespace chromeos #endif // CHROMEOS_DISPLAY_OUTPUT_CONFIGURATOR_H_