예제 #1
0
static bool
ply_renderer_head_add_connector (ply_renderer_head_t *head,
                                 drmModeConnector    *connector,
                                 int                  connector_mode_index)
{
  drmModeModeInfo *mode;

  mode = &connector->modes[connector_mode_index];

  if (mode->hdisplay != head->area.width || mode->vdisplay != head->area.height)
    {
      ply_trace ("Tried to add connector with resolution %dx%d to %dx%d head",
                 (int) mode->hdisplay, (int) mode->vdisplay,
                 (int) head->area.width, (int) head->area.height);
      return false;
    }
  else
    {
      ply_trace ("Adding connector with id %d to %dx%d head",
                 (int) connector->connector_id,
                 (int) head->area.width, (int) head->area.height);
    }

  ply_array_add_uint32_element (head->connector_ids, connector->connector_id);

  return true;
}
static bool
fetch_buffer (ply_renderer_driver_t *driver,
              uint32_t               buffer_id,
              unsigned long         *width,
              unsigned long         *height,
              unsigned long         *row_stride)
{
        ply_renderer_buffer_t *buffer;

        buffer = get_buffer_from_id (driver, buffer_id);

        if (buffer == NULL) {
                ply_trace ("could not fetch buffer %u", buffer_id);
                return false;
        }

        if (width != NULL)
                *width = buffer->width;

        if (height != NULL)
                *height = buffer->height;

        if (row_stride != NULL)
                *row_stride = buffer->row_stride;

        ply_trace ("fetched %ux%u buffer with stride %u",
                   buffer->width, buffer->height, buffer->row_stride);
        return true;
}
예제 #3
0
static bool
query_device (ply_renderer_backend_t *backend)
{
  assert (backend != NULL);
  assert (backend->device_fd >= 0);

  backend->resources = drmModeGetResources (backend->device_fd);

  if (backend->resources == NULL)
    {
      ply_trace ("Could not get card resources");
      return false;
    }

  if (!create_heads_for_active_connectors (backend))
    {
      ply_trace ("Could not initialize heads");
      return false;
    }

  if (!has_32bpp_support (backend))
    {
      ply_trace ("Device doesn't support 32bpp framebuffer");
      return false;
    }

  return true;
}
static uint32_t
create_buffer (ply_renderer_driver_t *driver,
               unsigned long          width,
               unsigned long          height,
               unsigned long         *row_stride)
{
        ply_renderer_buffer_t *buffer;

        buffer = ply_renderer_buffer_new (driver, width, height);

        if (buffer == NULL) {
                ply_trace ("Could not allocate GEM object for frame buffer: %m");
                return 0;
        }

        if (drmModeAddFB (driver->device_fd, width, height,
                          24, 32, buffer->row_stride, buffer->handle,
                          &buffer->id) != 0) {
                ply_trace ("Could not set up GEM object as frame buffer: %m");
                ply_renderer_buffer_free (driver, buffer);
                return 0;
        }

        *row_stride = buffer->row_stride;

        buffer->added_fb = true;
        ply_hashtable_insert (driver->buffers,
                              (void *) (uintptr_t) buffer->id,
                              buffer);

        return buffer->id;
}
예제 #5
0
static bool
show_splash_screen (ply_boot_splash_plugin_t *plugin,
                    ply_event_loop_t         *loop,
                    ply_buffer_t             *boot_buffer,
                    ply_boot_splash_mode_t    mode)
{
  assert (plugin != NULL);

  if (ply_list_get_length (plugin->displays) == 0)
    {
      ply_trace ("no pixel displays");
      return false;
    }

  plugin->loop = loop;
  plugin->mode = mode;

  ply_event_loop_watch_for_exit (loop, (ply_event_loop_exit_handler_t)
                                 detach_from_event_loop,
                                 plugin);

  ply_event_loop_watch_signal (plugin->loop,
                               SIGINT,
                               (ply_event_handler_t)
                               on_interrupt, plugin);

  ply_trace ("starting boot animation");
  return start_animation (plugin);
}
예제 #6
0
static void
ply_terminal_reopen_device (ply_terminal_t *terminal)
{
    ply_terminal_open_result_t open_result;

    ply_trace ("trying to reopen terminal '%s' (attempt %d)",
               terminal->name,
               terminal->number_of_reopen_tries);

    terminal->number_of_reopen_tries++;

    open_result = ply_terminal_open_device (terminal);

    if (open_result == PLY_TERMINAL_OPEN_RESULT_INCOMPLETE) {
        int total_retries;

        total_retries = (int) (PLY_TERMINAL_REOPEN_TIMEOUT / PLY_TERMINAL_REOPEN_INTERVAL);

        if (terminal->number_of_reopen_tries < total_retries) {
            ply_event_loop_watch_for_timeout (terminal->loop,
                                              PLY_TERMINAL_REOPEN_INTERVAL,
                                              (ply_event_loop_timeout_handler_t)
                                              ply_terminal_reopen_device,
                                              terminal);
        } else {
            ply_trace ("couldn't reopen tty, giving up");
            terminal->number_of_reopen_tries = 0;
        }
    }
}
예제 #7
0
파일: plymouth.c 프로젝트: pkt/pkt-plymouth
static bool
get_kernel_command_line (state_t *state)
{
  int fd;

  ply_trace ("opening /proc/cmdline");
  fd = open ("proc/cmdline", O_RDONLY);

  if (fd < 0)
    {
      ply_trace ("couldn't open it: %m");
      return false;
    }

  ply_trace ("reading kernel command line");
  if (read (fd, state->kernel_command_line, sizeof (state->kernel_command_line)) < 0)
    {
      ply_trace ("couldn't read it: %m");
      return false;
    }

  ply_trace ("Kernel command line is: '%s'", state->kernel_command_line);
  close (fd);
  return true;
}
예제 #8
0
void
ply_terminal_close (ply_terminal_t *terminal)
{
    if (!terminal->is_open) {
        ply_trace ("terminal %s is already closed", terminal->name);
        return;
    }

    terminal->is_open = false;

    ply_terminal_stop_watching_for_vt_changes (terminal);

    ply_trace ("restoring color palette");
    ply_terminal_restore_color_palette (terminal);

    if (terminal->fd_watch != NULL) {
        ply_trace ("stop watching tty fd");
        ply_event_loop_stop_watching_fd (terminal->loop, terminal->fd_watch);
        terminal->fd_watch = NULL;
    }

    if (terminal->loop != NULL) {
        ply_trace ("stop watching SIGWINCH signal");
        ply_event_loop_stop_watching_signal (terminal->loop, SIGWINCH);
    }

    ply_trace ("setting buffered input");
    ply_terminal_set_buffered_input (terminal);

    close (terminal->fd);
    terminal->fd = -1;
}
예제 #9
0
static bool
ply_renderer_head_set_scan_out_buffer (ply_renderer_backend_t *backend,
                                       ply_renderer_head_t    *head,
                                       uint32_t                buffer_id)
{
  drmModeModeInfo *mode;
  uint32_t *connector_ids;
  int number_of_connectors;

  connector_ids = (uint32_t *) ply_array_get_uint32_elements (head->connector_ids);
  number_of_connectors = ply_array_get_size (head->connector_ids);

  mode = &head->connector0->modes[head->connector0_mode_index];

  ply_trace ("Setting scan out buffer of %ldx%ld head to our buffer",
             head->area.width, head->area.height);

  /* Tell the controller to use the allocated scan out buffer on each connectors
  */
  if (drmModeSetCrtc (backend->device_fd, head->controller_id, buffer_id,
                      0, 0, connector_ids, number_of_connectors, mode) < 0)
    {
      ply_trace ("Couldn't set scan out buffer for head with controller id %d",
                 head->controller_id);
      return false;
    }

  return true;
}
예제 #10
0
static bool
open_device (ply_renderer_backend_t *backend)
{
  assert (backend != NULL);
  assert (backend->device_name != NULL);

  if (!load_driver (backend))
    return false;

  if (!ply_terminal_open (backend->terminal))
    {
      ply_trace ("could not open terminal: %m");
      return false;
    }

  if (!ply_terminal_is_vt (backend->terminal))
    {
      ply_trace ("terminal is not a VT");
      ply_terminal_close (backend->terminal);
      return false;
    }

  ply_terminal_watch_for_active_vt_change (backend->terminal,
                                           (ply_terminal_active_vt_changed_handler_t)
                                           on_active_vt_changed,
                                           backend);

  return true;
}
예제 #11
0
파일: plymouth.c 프로젝트: pkt/pkt-plymouth
static bool
answer_via_command (char           *command,
                    const char     *answer,
                    int            *exit_status)
{
  bool gave_answer;
  pid_t pid;
  int command_input_sender_fd, command_input_receiver_fd;


  ply_trace ("running command '%s'", command);

  /* answer may be NULL which means,
   * "The daemon can't ask the user questions,
   *   do all the prompting from the client"
   */

  gave_answer = false;
  if (answer != NULL &&
    !ply_open_unidirectional_pipe (&command_input_sender_fd,
                                   &command_input_receiver_fd))
    return false;

  pid = fork (); 

  if (pid < 0)
    return false;

  if (pid == 0)
    {
      char **args;
      args = split_string (command, ' ');
      if (answer != NULL)
        {
          close (command_input_sender_fd);
          dup2 (command_input_receiver_fd, STDIN_FILENO);
        }

      execvp (args[0], args); 
      ply_trace ("could not run command: %m");
      _exit (127);
    }

  if (answer != NULL)
    {
      close (command_input_receiver_fd);

      if (write (command_input_sender_fd, answer, strlen (answer)) < 0)
        goto out;
    }

  gave_answer = true;
out:
  if (answer != NULL)
    close (command_input_sender_fd);
  waitpid (pid, exit_status, 0); 

  return gave_answer;
}
예제 #12
0
static bool
ply_renderer_load_plugin (ply_renderer_t *renderer,
                          const char     *module_path)
{
  assert (renderer != NULL);

  get_backend_interface_function_t get_renderer_backend_interface;

  renderer->module_handle = ply_open_module (module_path);

  if (renderer->module_handle == NULL)
    return false;

  get_renderer_backend_interface = (get_backend_interface_function_t)
      ply_module_look_up_function (renderer->module_handle,
                                   "ply_renderer_backend_get_interface");

  if (get_renderer_backend_interface == NULL)
    {
      ply_save_errno ();
      ply_trace ("module '%s' is not a renderer plugin",
                 module_path);
      ply_close_module (renderer->module_handle);
      renderer->module_handle = NULL;
      ply_restore_errno ();
      return false;
    }

  renderer->plugin_interface = get_renderer_backend_interface ();

  if (renderer->plugin_interface == NULL)
    {
      ply_trace ("module '%s' is not a valid renderer plugin",
                 module_path);
      ply_save_errno ();
      ply_close_module (renderer->module_handle);
      renderer->module_handle = NULL;
      ply_restore_errno ();
      return false;
    }

  renderer->backend = renderer->plugin_interface->create_backend (renderer->device_name,
                                                                  renderer->terminal);

  if (renderer->backend == NULL)
    {
      ply_save_errno ();
      ply_trace ("module '%s' renderer backend could not be created",
                 module_path);
      ply_close_module (renderer->module_handle);
      renderer->module_handle = NULL;
      ply_restore_errno ();
      return false;
    }

  return true;
}
예제 #13
0
static bool
show_splash_screen (ply_boot_splash_plugin_t *plugin,
                    ply_event_loop_t         *loop,
                    ply_buffer_t             *boot_buffer,
                    ply_boot_splash_mode_t    mode)
{
  assert (plugin != NULL);

  plugin->loop = loop;
  plugin->mode = mode;

  ply_trace ("loading lock image");
  if (!ply_image_load (plugin->lock_image))
    return false;

  ply_trace ("loading box image");
  if (!ply_image_load (plugin->box_image))
    return false;

  if (plugin->corner_image != NULL)
    {
      ply_trace ("loading corner image");

      if (!ply_image_load (plugin->corner_image))
        {
          ply_image_free (plugin->corner_image);
          plugin->corner_image = NULL;
        }
    }

  if (!load_views (plugin))
    {
      ply_trace ("couldn't load views");
      return false;
    }

  ply_event_loop_watch_for_exit (loop, (ply_event_loop_exit_handler_t)
                                 detach_from_event_loop,
                                 plugin);

  ply_event_loop_watch_signal (plugin->loop,
                               SIGINT,
                               (ply_event_handler_t)
                               on_interrupt, plugin);

  ply_trace ("starting boot animation");
  start_progress_animation (plugin);

  plugin->is_visible = true;

  return true;
}
예제 #14
0
파일: plugin.c 프로젝트: pkt/pkt-plymouth
static bool
load_driver (ply_renderer_backend_t *backend)
{
  char *driver_name;
  int device_fd;

  driver_name = find_driver_for_device (backend->device_name);
  ply_trace ("Attempting to load driver '%s'", driver_name);
  device_fd = drmOpen (driver_name, NULL);

  if (device_fd < 0)
    {
      ply_trace ("drmOpen failed");
      free (driver_name);
      return false;
    }

  if (strcmp (driver_name, "i915") == 0)
    {
      backend->driver_interface = ply_renderer_i915_driver_get_interface ();
      backend->driver_supports_mapping_console = true;
    }
  else if (strcmp (driver_name, "radeon") == 0)
    {
      backend->driver_interface = ply_renderer_radeon_driver_get_interface ();
      backend->driver_supports_mapping_console = false;
    }
  else if (strcmp (driver_name, "nouveau") == 0)
    {
      backend->driver_interface = ply_renderer_nouveau_driver_get_interface ();
      backend->driver_supports_mapping_console = false;
    }
  free (driver_name);

  if (backend->driver_interface == NULL)
    {
      close (device_fd);
      return false;
    }

  backend->driver = backend->driver_interface->create_driver (device_fd);

  if (backend->driver == NULL)
    {
      close (device_fd);
      return false;
    }

  backend->device_fd = device_fd;

  return true;
}
예제 #15
0
static bool
view_load (view_t *view)
{
  ply_trace ("loading entry");
  if (!ply_entry_load (view->entry))
    return false;

  ply_trace ("loading throbber");
  if (!ply_throbber_load (view->throbber))
    return false;

  return true;
}
예제 #16
0
파일: plymouth.c 프로젝트: pkt/pkt-plymouth
static void
on_password_answer_failure (password_answer_state_t *answer_state,
                            ply_boot_client_t       *client)
{
  ply_trace ("password answer failure");

  /* plymouthd isn't running for some reason.  If there is a command
   * to run, we'll run it anyway, because it might be important for
   * boot up to continue (to decrypt the root partition or whatever)
   */
  if (answer_state->command != NULL)
    {
      int exit_status;
      bool command_started;

      ply_trace ("daemon not available, running command on our own");

      exit_status = 127;
      command_started = false;
      while (answer_state->number_of_tries_left > 0)
        {
          command_started = answer_via_command (answer_state->command, NULL,
                                                &exit_status);

          if (command_started && WIFEXITED (exit_status) &&
              WEXITSTATUS (exit_status) == 0)
            {
              ply_trace ("command was successful");
              break;
            }

          ply_trace ("command failed");
          answer_state->number_of_tries_left--;
        }

      if (command_started && WIFSIGNALED (exit_status))
        {
          ply_trace ("command died with signal %s", strsignal (WTERMSIG (exit_status)));
          raise (WTERMSIG (exit_status));
        }
      else
        {
          ply_event_loop_exit (answer_state->state->loop,
                               WEXITSTATUS (exit_status));
        }
    }
  else
    {
      ply_event_loop_exit (answer_state->state->loop, 1);
    }
}
예제 #17
0
파일: plugin.c 프로젝트: halfline/plymouth
static void
become_idle (ply_boot_splash_plugin_t *plugin,
             ply_trigger_t            *idle_trigger)
{
        ply_trace ("deactivation requested");
        if (plugin->is_idle) {
                ply_trace ("plugin is already idle");
                ply_trigger_pull (idle_trigger, NULL);
                return;
        }

        stop_animation (plugin, idle_trigger);
        plugin->is_idle = true;
}
예제 #18
0
static void
on_active_vt_changed (ply_renderer_backend_t *backend)
{
  if (ply_terminal_is_active (backend->terminal))
    {
      ply_trace ("activating on vt change");
      activate (backend);
    }
  else
    {
      ply_trace ("deactivating on vt change");
      deactivate (backend);
    }
}
예제 #19
0
파일: plymouth.c 프로젝트: pkt/pkt-plymouth
static void
on_multiple_password_answers (password_answer_state_t     *answer_state,
                              const char * const          *answers)
{
  bool need_to_ask_user;
  int i;
  int exit_status;

  assert (answer_state->command != NULL);

  ply_trace ("on multiple password answers");

  need_to_ask_user = true;

  if (answers != NULL)
    {
      ply_trace ("daemon has a few candidate passwords for us to try");
      for (i = 0; answers[i] != NULL; i++)
        {
          bool command_started;
          exit_status = 127;
          command_started = answer_via_command (answer_state->command, answers[i],
                                                &exit_status);
          if (command_started && WIFEXITED (exit_status) &&
              WEXITSTATUS (exit_status) == 0)
            {
              need_to_ask_user = false;
              break;
            }
        }
    }
  else
    {
      ply_trace ("daemon has no candidate passwords for us to try");
    }

  if (need_to_ask_user)
    {
      ply_boot_client_ask_daemon_for_password (answer_state->state->client,
                                               answer_state->prompt,
                                               (ply_boot_client_answer_handler_t)
                                               on_password_answer,
                                               (ply_boot_client_response_handler_t)
                                               on_password_answer_failure, answer_state);
      return;
    }

  ply_event_loop_exit (answer_state->state->loop, 0);
}
예제 #20
0
bool
ply_renderer_open (ply_renderer_t *renderer)
{
  int i;

  /* FIXME: at some point we may want to make this
   * part more dynamic (so you don't have to edit this
   * list to add a new renderer)
   */
  const char *known_plugins[] =
    {
      PLYMOUTH_PLUGIN_PATH "renderers/x11.so",
      PLYMOUTH_PLUGIN_PATH "renderers/drm.so",
      PLYMOUTH_PLUGIN_PATH "renderers/frame-buffer.so",
      NULL
    };

  for (i = 0; known_plugins[i] != NULL; i++)
    {
      const char *plugin_path;

      plugin_path = known_plugins[i];

      if (!ply_renderer_load_plugin (renderer, plugin_path))
        continue;

      if (!ply_renderer_open_device (renderer))
        {
          ply_trace ("could not open rendering device for plugin %s",
                     plugin_path);
          ply_renderer_unload_plugin (renderer);
          continue;
        }

      if (!ply_renderer_query_device (renderer))
        {
          ply_trace ("could not query rendering device for plugin %s",
                     plugin_path);
          ply_renderer_close_device (renderer);
          ply_renderer_unload_plugin (renderer);
          continue;
        }

      return true;
  }

  ply_trace ("could not find suitable rendering plugin");
  return false;
}
예제 #21
0
static bool
ply_renderer_head_map (ply_renderer_backend_t *backend,
                       ply_renderer_head_t    *head)
{
  bool scan_out_set;

  assert (backend != NULL);
  assert (backend->device_fd >= 0);
  assert (backend->driver_interface != NULL);
  assert (backend->driver != NULL);

  assert (head != NULL);

  ply_trace ("Creating buffer for %ldx%ld renderer head", head->area.width, head->area.height);
  head->scan_out_buffer_id =
    backend->driver_interface->create_buffer (backend->driver,
                                              head->area.width, head->area.height,
                                              &head->row_stride);

  if (head->scan_out_buffer_id == 0)
    return false;

  ply_trace ("Mapping buffer for %ldx%ld renderer head", head->area.width, head->area.height);
  if (!backend->driver_interface->map_buffer (backend->driver,
                                              head->scan_out_buffer_id))
    {
      backend->driver_interface->destroy_buffer (backend->driver,
                                                 head->scan_out_buffer_id);
      head->scan_out_buffer_id = 0;
      return false;
    }

  /* FIXME: Maybe we should blit the fbcon contents instead of the (blank)
   * shadow buffer?
   */
  ply_renderer_head_redraw (backend, head);

  scan_out_set = reset_scan_out_buffer_if_needed (backend, head);
  if (!scan_out_set && backend->is_active)
    {
      backend->driver_interface->destroy_buffer (backend->driver,
                                                 head->scan_out_buffer_id);
      head->scan_out_buffer_id = 0;
      return false;
    }

  return true;
}
예제 #22
0
static void
ply_boot_connection_on_keystroke_answer (ply_boot_connection_t *connection,
                                        const char            *key)
{
  ply_trace ("got key: %s", key);
  ply_boot_connection_send_answer (connection, key);
}
예제 #23
0
static void
ply_boot_connection_on_question_answer (ply_boot_connection_t *connection,
                                        const char             *answer)
{
  ply_trace ("got question answer: %s", answer);
  ply_boot_connection_send_answer (connection, answer);
}
예제 #24
0
파일: plugin.c 프로젝트: halfline/plymouth
static void
remove_pixel_display (ply_boot_splash_plugin_t *plugin,
                      ply_pixel_display_t      *display)
{
        ply_list_node_t *node;

        ply_trace ("removing pixel display from plugin");
        node = ply_list_get_first_node (plugin->views);
        while (node != NULL) {
                view_t *view;
                ply_list_node_t *next_node;

                view = ply_list_node_get_data (node);
                next_node = ply_list_get_next_node (plugin->views, node);

                if (view->display == display) {
                        ply_pixel_display_set_draw_handler (view->display, NULL, NULL);
                        view_free (view);
                        ply_list_remove_node (plugin->views, node);
                        return;
                }

                node = next_node;
        }
}
예제 #25
0
파일: plugin.c 프로젝트: halfline/plymouth
static void
start_animation (ply_boot_splash_plugin_t *plugin)
{
        ply_list_node_t *node;

        if (plugin->is_animating)
                return;

        ply_trace ("starting animation");

        node = ply_list_get_first_node (plugin->views);
        while (node != NULL) {
                ply_list_node_t *next_node;
                view_t *view;

                view = ply_list_node_get_data (node);
                next_node = ply_list_get_next_node (plugin->views, node);

                view_start_animation (view);

                node = next_node;
        }

        plugin->is_animating = true;

        if (plugin->mode == PLY_BOOT_SPLASH_MODE_SHUTDOWN)
                plugin->is_idle = true;
}
예제 #26
0
static bool
ply_renderer_buffer_map (ply_renderer_driver_t *driver,
                         ply_renderer_buffer_t *buffer)
{
        struct drm_mode_map_dumb map_dumb_buffer_request;
        void *map_address;

        if (buffer->map_address != MAP_FAILED) {
                buffer->map_count++;
                return true;
        }

        memset (&map_dumb_buffer_request, 0, sizeof(struct drm_mode_map_dumb));
        map_dumb_buffer_request.handle = buffer->handle;
        if (drmIoctl (driver->device_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb_buffer_request) < 0) {
                ply_trace ("Could not map GEM object %u: %m", buffer->handle);
                return false;
        }

        map_address = mmap (0, buffer->map_size,
                            PROT_READ | PROT_WRITE, MAP_SHARED,
                            driver->device_fd, map_dumb_buffer_request.offset);

        if (map_address == MAP_FAILED)
                return false;

        buffer->map_address = map_address;
        buffer->map_count++;

        return true;
}
예제 #27
0
static void
activate (ply_renderer_backend_t *backend)
{
  ply_list_node_t *node;

  ply_trace ("taking master and scanning out");
  backend->is_active = true;

  drmSetMaster (backend->device_fd);
  node = ply_list_get_first_node (backend->heads);
  while (node != NULL)
    {
      ply_list_node_t *next_node;
      ply_renderer_head_t *head;

      head = (ply_renderer_head_t *) ply_list_node_get_data (node);
      next_node = ply_list_get_next_node (backend->heads, node);

      if (head->scan_out_buffer_id != 0)
        {
          /* Flush out any pending drawing to the buffer
           */
          flush_head (backend, head);

          /* Then send the buffer to the monitor
           */
          ply_renderer_head_set_scan_out_buffer (backend, head,
                                                 head->scan_out_buffer_id);
        }

      node = next_node;
    }
}
예제 #28
0
static void
deactivate (ply_renderer_backend_t *backend)
{
  ply_trace ("dropping master");
  drmDropMaster (backend->device_fd);
  backend->is_active = false;
}
예제 #29
0
파일: plugin.c 프로젝트: magcius/plymouth
static void
detach_from_event_loop (ply_boot_splash_plugin_t *plugin)
{
        plugin->loop = NULL;

        ply_trace ("detaching from event loop");
}
예제 #30
0
static int
get_index_of_active_mode (ply_renderer_backend_t *backend,
                          drmModeCrtc            *controller,
                          drmModeConnector       *connector)
{
  if (!controller->mode_valid)
    {
      ply_trace ("No valid mode currently active on monitor");
      return -1;
    }

  ply_trace ("Looking for connector mode index of active mode %dx%d",
             controller->mode.hdisplay, controller->mode.vdisplay);

  return find_index_of_mode (backend, connector, &controller->mode);
}