/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */

/*
 * Copyright (C) 2001 Havoc Pennington
 * Copyright (C) 2003 Rob Adams
 * Copyright (C) 2004-2006 Elijah Newren
 * Copyright (C) 2013 Red Hat Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 */

#ifndef META_MONITOR_MANAGER_PRIVATE_H
#define META_MONITOR_MANAGER_PRIVATE_H

#include <cogl/cogl.h>
#include <graphene.h>
#include <libgnome-desktop/gnome-pnp-ids.h>

#include "backends/meta-backend-private.h"
#include "backends/meta-cursor.h"
#include "backends/meta-display-config-shared.h"
#include "backends/meta-monitor-transform.h"
#include "core/util-private.h"
#include "meta/display.h"
#include "meta/meta-monitor-manager.h"

#include "meta-dbus-display-config.h"

#define META_MONITOR_MANAGER_MIN_SCREEN_WIDTH 640
#define META_MONITOR_MANAGER_MIN_SCREEN_HEIGHT 480

typedef enum _MetaMonitorManagerCapability
{
  META_MONITOR_MANAGER_CAPABILITY_NONE = 0,
  META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE = (1 << 0),
  META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED = (1 << 1)
} MetaMonitorManagerCapability;

/* Equivalent to the 'method' enum in org.gnome.Mutter.DisplayConfig */
typedef enum _MetaMonitorsConfigMethod
{
  META_MONITORS_CONFIG_METHOD_VERIFY = 0,
  META_MONITORS_CONFIG_METHOD_TEMPORARY = 1,
  META_MONITORS_CONFIG_METHOD_PERSISTENT = 2
} MetaMonitorsConfigMethod;

/* Equivalent to the 'layout-mode' enum in org.gnome.Mutter.DisplayConfig */
typedef enum _MetaLogicalMonitorLayoutMode
{
  META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL = 1,
  META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL = 2
} MetaLogicalMonitorLayoutMode;

/*
 * MetaCrtcAssignment:
 *
 * A representation of a CRTC configuration, generated by
 * MetaMonitorConfigManager.
 */
struct _MetaCrtcAssignment
{
  MetaCrtc                 *crtc;
  MetaCrtcMode             *mode;
  graphene_rect_t           layout;
  MetaMonitorTransform      transform;
  GPtrArray                *outputs;
};

/*
 * MetaOutputAssignment:
 *
 * A representation of a connector configuration, generated by
 * MetaMonitorConfigManager.
 */
struct _MetaOutputAssignment
{
  MetaOutput  *output;
  gboolean     is_primary;
  gboolean     is_presentation;
  gboolean     is_underscanning;
};

#define META_TYPE_MONITOR_MANAGER            (meta_monitor_manager_get_type ())
#define META_MONITOR_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_MONITOR_MANAGER, MetaMonitorManager))
#define META_MONITOR_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  META_TYPE_MONITOR_MANAGER, MetaMonitorManagerClass))
#define META_IS_MONITOR_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_MONITOR_MANAGER))
#define META_IS_MONITOR_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  META_TYPE_MONITOR_MANAGER))
#define META_MONITOR_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  META_TYPE_MONITOR_MANAGER, MetaMonitorManagerClass))

G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaMonitorManager, g_object_unref)

struct _MetaMonitorManager
{
  GObject parent_instance;

  MetaDBusDisplayConfig *display_config;

  MetaBackend *backend;

  /* XXX: this structure is very badly
     packed, but I like the logical organization
     of fields */

  gboolean in_init;
  unsigned int serial;

  MetaLogicalMonitorLayoutMode layout_mode;

  int screen_width;
  int screen_height;

  GList *monitors;

  GList *logical_monitors;
  MetaLogicalMonitor *primary_logical_monitor;

  int dbus_name_id;

  int persistent_timeout_id;

  MetaMonitorConfigManager *config_manager;

  GnomePnpIds *pnp_ids;

  gulong experimental_features_changed_handler_id;

  MetaMonitorSwitchConfigType current_switch_config;
};

/**
 * MetaMonitorManagerClass:
 *
 * @read_edid: Returns the raw Extended Display Identification Data (EDID)
 *   for the given #MetaOutput object. Use meta_output_parse_edid() to parse
 *   afterwards.
 *
 * @ensure_initial_config: Called on setup. Makes sure an initial config
 *   is loaded.
 *
 * @apply_monitors_config: Tries to apply the given config using the given
 *   method. Throws an error if something went wrong.
 *
 * @set_power_save_mode: Sets the #MetaPowerSave mode (for all displays).
 *
 * @change_backlight: Changes the backlight intensity to the given value (in
 *   percent).
 *
 * @get_crtc_gamma: Queries and returns the gamma rampQueries and returns the
 *   gamma ramp.
 *
 * @set_crtc_gamma: Sets custom display LUT (look up table) for each primary
 *   color. Each table is indexed by a value that represents input intensity,
 *   and yields a value that represents output intensity.
 *
 * @tiled_monitor_added: Should be called by a #MetaMonitor when it is created.
 *
 * @tiled_monitor_removed: Should be called by a #MetaMonitor when it is
 *   destroyed.
 *
 * @is_transform_handled: vfunc for
 *   meta_monitor_manager_is_transform_handled().
 * @calculate_monitor_mode_scale: vfunc for
 *   meta_monitor_manager_calculate_monitor_mode_scale().
 * @calculate_supported_scales: vfunc for
 *   meta_monitor_manager_calculate_supported_scales().
 * @get_capabilities: vfunc for meta_monitor_manager_get_capabilities().
 * @get_max_screen_size: vfunc for meta_monitor_manager_get_max_screen_size().
 * @get_default_layout_mode: vfunc for meta_monitor_manager_get_default_layout_mode().
 *
 * The base class for a #MetaMonitorManager.
 */
struct _MetaMonitorManagerClass
{
  MetaDBusDisplayConfigSkeletonClass parent_class;

  GBytes* (*read_edid) (MetaMonitorManager *,
                        MetaOutput         *);

  void (*read_current_state) (MetaMonitorManager *);

  void (*ensure_initial_config) (MetaMonitorManager *);

  gboolean (*apply_monitors_config) (MetaMonitorManager      *,
                                     MetaMonitorsConfig      *,
                                     MetaMonitorsConfigMethod ,
                                     GError                 **);

  void (*set_power_save_mode) (MetaMonitorManager *,
                               MetaPowerSave);

  void (*change_backlight) (MetaMonitorManager *,
                            MetaOutput         *,
                            int);

  void (*get_crtc_gamma) (MetaMonitorManager  *,
                          MetaCrtc            *,
                          gsize               *,
                          unsigned short     **,
                          unsigned short     **,
                          unsigned short     **);
  void (*set_crtc_gamma) (MetaMonitorManager *,
                          MetaCrtc           *,
                          gsize               ,
                          unsigned short     *,
                          unsigned short     *,
                          unsigned short     *);

  void (*tiled_monitor_added) (MetaMonitorManager *,
                               MetaMonitor        *);

  void (*tiled_monitor_removed) (MetaMonitorManager *,
                                 MetaMonitor        *);

  gboolean (*is_transform_handled) (MetaMonitorManager  *,
                                    MetaCrtc            *,
                                    MetaMonitorTransform);

  float (*calculate_monitor_mode_scale) (MetaMonitorManager *,
                                         MetaMonitor        *,
                                         MetaMonitorMode    *);

  float * (*calculate_supported_scales) (MetaMonitorManager          *,
                                         MetaLogicalMonitorLayoutMode ,
                                         MetaMonitor                 *,
                                         MetaMonitorMode             *,
                                         int                         *);

  MetaMonitorManagerCapability (*get_capabilities) (MetaMonitorManager *);

  gboolean (*get_max_screen_size) (MetaMonitorManager *,
                                   int                *,
                                   int                *);

  MetaLogicalMonitorLayoutMode (*get_default_layout_mode) (MetaMonitorManager *);
};

META_EXPORT_TEST
MetaBackend *       meta_monitor_manager_get_backend (MetaMonitorManager *manager);

void                meta_monitor_manager_setup (MetaMonitorManager *manager);

META_EXPORT_TEST
void                meta_monitor_manager_rebuild (MetaMonitorManager *manager,
                                                  MetaMonitorsConfig *config);

META_EXPORT_TEST
void                meta_monitor_manager_rebuild_derived (MetaMonitorManager *manager,
                                                          MetaMonitorsConfig *config);

META_EXPORT_TEST
int                 meta_monitor_manager_get_num_logical_monitors (MetaMonitorManager *manager);

META_EXPORT_TEST
GList *             meta_monitor_manager_get_logical_monitors (MetaMonitorManager *manager);

MetaLogicalMonitor *meta_monitor_manager_get_logical_monitor_from_number (MetaMonitorManager *manager,
                                                                          int                 number);

MetaLogicalMonitor *meta_monitor_manager_get_primary_logical_monitor (MetaMonitorManager *manager);

MetaLogicalMonitor *meta_monitor_manager_get_logical_monitor_at (MetaMonitorManager *manager,
                                                                 float               x,
                                                                 float               y);

MetaLogicalMonitor *meta_monitor_manager_get_logical_monitor_from_rect (MetaMonitorManager *manager,
                                                                        MetaRectangle      *rect);

MetaLogicalMonitor *meta_monitor_manager_get_logical_monitor_neighbor (MetaMonitorManager  *manager,
                                                                       MetaLogicalMonitor  *logical_monitor,
                                                                       MetaDisplayDirection direction);

MetaMonitor *       meta_monitor_manager_get_primary_monitor (MetaMonitorManager *manager);

MetaMonitor *       meta_monitor_manager_get_laptop_panel (MetaMonitorManager *manager);

MetaMonitor *       meta_monitor_manager_get_monitor_from_spec (MetaMonitorManager *manager,
                                                                MetaMonitorSpec    *monitor_spec);

MetaMonitor *       meta_monitor_manager_get_monitor_from_connector (MetaMonitorManager *manager,
                                                                     const char         *connector);

META_EXPORT_TEST
GList *             meta_monitor_manager_get_monitors      (MetaMonitorManager *manager);

void                meta_monitor_manager_get_screen_size   (MetaMonitorManager *manager,
                                                            int                *width,
                                                            int                *height);

MetaPowerSave       meta_monitor_manager_get_power_save_mode (MetaMonitorManager *manager);

void                meta_monitor_manager_power_save_mode_changed (MetaMonitorManager *manager,
                                                                  MetaPowerSave       mode);

void                meta_monitor_manager_confirm_configuration (MetaMonitorManager *manager,
                                                                gboolean            ok);

void               meta_output_parse_edid (MetaOutput *output,
                                           GBytes     *edid);
gboolean           meta_output_is_laptop  (MetaOutput *output);

gboolean           meta_monitor_manager_has_hotplug_mode_update (MetaMonitorManager *manager);

META_EXPORT_TEST
void               meta_monitor_manager_read_current_state (MetaMonitorManager *manager);

META_EXPORT_TEST
void               meta_monitor_manager_on_hotplug (MetaMonitorManager *manager);

gboolean           meta_monitor_manager_get_monitor_matrix (MetaMonitorManager *manager,
                                                            MetaMonitor        *monitor,
                                                            MetaLogicalMonitor *logical_monitor,
                                                            gfloat              matrix[6]);

void               meta_monitor_manager_tiled_monitor_added (MetaMonitorManager *manager,
                                                             MetaMonitor        *monitor);
void               meta_monitor_manager_tiled_monitor_removed (MetaMonitorManager *manager,
                                                               MetaMonitor        *monitor);

gboolean           meta_monitor_manager_is_transform_handled (MetaMonitorManager  *manager,
                                                              MetaCrtc            *crtc,
                                                              MetaMonitorTransform transform);

META_EXPORT_TEST
MetaMonitorsConfig * meta_monitor_manager_ensure_configured (MetaMonitorManager *manager);

META_EXPORT_TEST
void               meta_monitor_manager_update_logical_state (MetaMonitorManager *manager,
                                                              MetaMonitorsConfig *config);

META_EXPORT_TEST
void               meta_monitor_manager_update_logical_state_derived (MetaMonitorManager *manager,
                                                                      MetaMonitorsConfig *config);

META_EXPORT_TEST
void               meta_monitor_manager_lid_is_closed_changed (MetaMonitorManager *manager);

gboolean           meta_monitor_manager_is_headless (MetaMonitorManager *manager);

float              meta_monitor_manager_calculate_monitor_mode_scale (MetaMonitorManager *manager,
                                                                      MetaMonitor        *monitor,
                                                                      MetaMonitorMode    *monitor_mode);

float *            meta_monitor_manager_calculate_supported_scales (MetaMonitorManager          *,
                                                                    MetaLogicalMonitorLayoutMode ,
                                                                    MetaMonitor                 *,
                                                                    MetaMonitorMode             *,
                                                                    int                         *);

gboolean           meta_monitor_manager_is_scale_supported (MetaMonitorManager          *manager,
                                                            MetaLogicalMonitorLayoutMode layout_mode,
                                                            MetaMonitor                 *monitor,
                                                            MetaMonitorMode             *monitor_mode,
                                                            float                        scale);

MetaMonitorManagerCapability
                   meta_monitor_manager_get_capabilities (MetaMonitorManager *manager);

gboolean           meta_monitor_manager_get_max_screen_size (MetaMonitorManager *manager,
                                                             int                *max_width,
                                                             int                *max_height);

MetaLogicalMonitorLayoutMode
                   meta_monitor_manager_get_default_layout_mode (MetaMonitorManager *manager);

MetaMonitorConfigManager *
                   meta_monitor_manager_get_config_manager (MetaMonitorManager *manager);

void meta_monitor_manager_rotate_monitor (MetaMonitorManager *manager);

void meta_monitor_manager_clear_output (MetaOutput *output);
void meta_monitor_manager_clear_mode (MetaCrtcMode *mode);
void meta_monitor_manager_clear_crtc (MetaCrtc *crtc);

gboolean meta_monitor_has_aspect_as_size (MetaMonitor *monitor);

char * meta_monitor_manager_get_vendor_name (MetaMonitorManager *manager,
                                             const char         *vendor);

static inline MetaOutputAssignment *
meta_find_output_assignment (MetaOutputAssignment **outputs,
                             unsigned int           n_outputs,
                             MetaOutput            *output)
{
  unsigned int i;

  for (i = 0; i < n_outputs; i++)
    {
      MetaOutputAssignment *output_assignment = outputs[i];

      if (output == output_assignment->output)
        return output_assignment;
    }

  return NULL;
}

#endif /* META_MONITOR_MANAGER_PRIVATE_H */
