| From 7347bc7a3b2ee0bf8226ba2f15ae4c6a02110bdd Mon Sep 17 00:00:00 2001 | 
| From: Jeffy Chen <jeffy.chen@rock-chips.com> | 
| Date: Thu, 12 Nov 2020 16:59:50 +0800 | 
| Subject: [PATCH 28/74] backend-drm: Add dummy output when no screens connected | 
|   | 
| Some clients are not expecting no screens, add a dummy output for them. | 
|   | 
| Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com> | 
| --- | 
|  compositor/main.c                    |   3 + | 
|  desktop-shell/shell.c                |   7 +- | 
|  libweston/backend-drm/drm-internal.h |   3 + | 
|  libweston/backend-drm/drm.c          | 203 ++++++++++++++++++++++++++- | 
|  libweston/backend-drm/kms.c          |   3 + | 
|  libweston/compositor.c               |   3 + | 
|  6 files changed, 216 insertions(+), 6 deletions(-) | 
|   | 
| diff --git a/compositor/main.c b/compositor/main.c | 
| index 128016b..3824404 100644 | 
| --- a/compositor/main.c | 
| +++ b/compositor/main.c | 
| @@ -2337,6 +2337,9 @@ drm_heads_changed(struct wl_listener *listener, void *arg) | 
|       * output. | 
|       */ | 
|      while ((head = weston_compositor_iterate_heads(compositor, head))) { | 
| +        if (!strcasecmp(weston_head_get_name(head), "dummy")) | 
| +            continue; | 
| + | 
|          drm_head_update_output_section(head); | 
|   | 
|          connected = weston_head_is_connected(head); | 
| diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c | 
| index aedb1e9..c57e7ef 100644 | 
| --- a/desktop-shell/shell.c | 
| +++ b/desktop-shell/shell.c | 
| @@ -4820,10 +4820,6 @@ shell_reposition_view_on_output_change(struct weston_view *view) | 
|      shsurf = get_shell_surface(view->surface); | 
|      if (!shsurf) | 
|          return; | 
| - | 
| -    shsurf->saved_position_valid = false; | 
| -    set_maximized(shsurf, false); | 
| -    set_fullscreen(shsurf, false, NULL); | 
|  } | 
|   | 
|  void | 
| @@ -4955,6 +4951,9 @@ handle_output_resized(struct wl_listener *listener, void *data) | 
|      struct weston_output *output = (struct weston_output *)data; | 
|      struct shell_output *sh_output = find_shell_output_from_weston_output(shell, output); | 
|   | 
| +    if (!sh_output) | 
| +        return; | 
| + | 
|      if (shell->lock_surface) | 
|          shell->lock_surface->committed(shell->lock_surface, 0, 0); | 
|   | 
| diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h | 
| index 9d24017..67e6dd2 100644 | 
| --- a/libweston/backend-drm/drm-internal.h | 
| +++ b/libweston/backend-drm/drm-internal.h | 
| @@ -354,6 +354,9 @@ struct drm_backend { | 
|   | 
|      struct wl_event_source *config_timer; | 
|      struct stat config_stat; | 
| + | 
| +    struct weston_output *dummy_output; | 
| +    struct drm_head *dummy_head; | 
|  }; | 
|   | 
|  struct drm_mode { | 
| diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c | 
| index 53a3d49..e829d77 100644 | 
| --- a/libweston/backend-drm/drm.c | 
| +++ b/libweston/backend-drm/drm.c | 
| @@ -210,8 +210,14 @@ drm_backend_update_outputs(struct drm_backend *b) | 
|  { | 
|      struct weston_output *base, *primary; | 
|   | 
| -    if (!b->primary_head) | 
| +    if (!b->primary_head) { | 
| +        if (!b->dummy_output->enabled) | 
| +            weston_output_enable(b->dummy_output); | 
|          return; | 
| +    } else { | 
| +        if (b->dummy_output->enabled) | 
| +            weston_output_disable(b->dummy_output); | 
| +    } | 
|   | 
|      primary = b->primary_head->base.output; | 
|   | 
| @@ -1245,7 +1251,7 @@ drm_output_find_special_plane(struct drm_backend *b, struct drm_output *output, | 
|              num_primary - 1 != output->crtc->pipe) | 
|              continue; | 
|   | 
| -        if (plane->type != type) | 
| +        if (!plane->plane_id || plane->type != type) | 
|              continue; | 
|          if (!drm_plane_is_available(plane, output)) | 
|              continue; | 
| @@ -2591,11 +2597,15 @@ drm_head_destroy(struct drm_head *head) | 
|  { | 
|      weston_head_release(&head->base); | 
|   | 
| +    if (!head->connector.connector_id) | 
| +        goto out; | 
| + | 
|      drm_connector_fini(&head->connector); | 
|   | 
|      if (head->backlight) | 
|          backlight_destroy(head->backlight); | 
|   | 
| +out: | 
|      free(head); | 
|  } | 
|   | 
| @@ -2827,6 +2837,9 @@ drm_backend_update_connectors(struct drm_backend *b, struct udev_device *drm_dev | 
|      wl_list_for_each_safe(base, base_next, | 
|                    &b->compositor->head_list, compositor_link) { | 
|          head = to_drm_head(base); | 
| +        if (!head->connector.connector_id) | 
| +            continue; | 
| + | 
|          connector_id = head->connector.connector_id; | 
|   | 
|          if (resources_has_connector(resources, connector_id)) | 
| @@ -2871,6 +2884,9 @@ drm_backend_update_connectors(struct drm_backend *b, struct udev_device *drm_dev | 
|                  continue; | 
|   | 
|              head = to_drm_head(base); | 
| +            if (!head->connector.connector_id) | 
| +                continue; | 
| + | 
|              conn = head->connector.conn; | 
|   | 
|              if (conn->connection != DRM_MODE_CONNECTED || | 
| @@ -2900,6 +2916,8 @@ drm_backend_update_connectors(struct drm_backend *b, struct udev_device *drm_dev | 
|      } | 
|  match_done: | 
|   | 
| +    weston_head_set_connection_status(&b->dummy_head->base, | 
| +                      !b->primary_head); | 
|      drm_backend_update_outputs(b); | 
|   | 
|      weston_compositor_read_presentation_clock(b->compositor, &now); | 
| @@ -3072,6 +3090,8 @@ drm_destroy(struct weston_compositor *ec) | 
|      struct drm_crtc *crtc, *crtc_tmp; | 
|      struct drm_writeback *writeback, *writeback_tmp; | 
|   | 
| +    weston_output_destroy(b->dummy_output); | 
| + | 
|      udev_input_destroy(&b->input); | 
|   | 
|      wl_event_source_remove(b->config_timer); | 
| @@ -3776,6 +3796,180 @@ config_timer_handler(void *data) | 
|      return 0; | 
|  } | 
|   | 
| +static int | 
| +drm_dummy_output_start_repaint_loop(struct weston_output *output_base) | 
| +{ | 
| +    weston_output_finish_frame(output_base, NULL, | 
| +                   WP_PRESENTATION_FEEDBACK_INVALID); | 
| + | 
| +    return 0; | 
| +} | 
| + | 
| +static int | 
| +drm_dummy_output_repaint(struct weston_output *output_base, | 
| +               pixman_region32_t *damage, | 
| +               void *repaint_data) | 
| +{ | 
| +    struct drm_backend *b = to_drm_backend(output_base->compositor); | 
| + | 
| +    wl_signal_emit(&output_base->frame_signal, damage); | 
| + | 
| +    if (b->use_pixman) | 
| +        return -1; | 
| + | 
| +    /* Switch GL output context to avoid corruption */ | 
| +    output_base->compositor->renderer->repaint_output(output_base, damage); | 
| +    return -1; | 
| +} | 
| + | 
| +static int | 
| +drm_dummy_output_enable(struct weston_output *output_base) | 
| +{ | 
| +    struct drm_backend *b = to_drm_backend(output_base->compositor); | 
| +    struct drm_output *output = to_drm_output(output_base); | 
| + | 
| +    if (b->use_pixman) | 
| +        return 0; | 
| + | 
| +    return drm_output_init_egl(output, b); | 
| +} | 
| + | 
| +static int | 
| +drm_dummy_output_disable(struct weston_output *output_base) | 
| +{ | 
| +    struct drm_backend *b = to_drm_backend(output_base->compositor); | 
| +    struct drm_output *output = to_drm_output(output_base); | 
| + | 
| +    if (!b->use_pixman) | 
| +        drm_output_fini_egl(output); | 
| + | 
| +    return 0; | 
| +} | 
| + | 
| +static void | 
| +drm_dummy_output_destroy(struct weston_output *output_base) | 
| +{ | 
| +    struct drm_output *output = to_drm_output(output_base); | 
| +    struct drm_plane *plane = output->scanout_plane; | 
| +    struct weston_mode *mode, *next; | 
| + | 
| +    if (output->base.enabled) | 
| +        drm_dummy_output_disable(&output->base); | 
| + | 
| +    wl_list_for_each_safe(mode, next, &output_base->mode_list, link) { | 
| +        wl_list_remove(&mode->link); | 
| +        free(mode); | 
| +    } | 
| + | 
| +    drm_plane_state_free(plane->state_cur, true); | 
| +    weston_plane_release(&plane->base); | 
| +    wl_list_remove(&plane->link); | 
| +    weston_drm_format_array_fini(&plane->formats); | 
| +    free(plane); | 
| + | 
| +    weston_output_release(output_base); | 
| +    free(output); | 
| +} | 
| + | 
| +static struct weston_output * | 
| +drm_dummy_output_create(struct drm_backend *b) | 
| +{ | 
| +    struct drm_output *output; | 
| +    struct drm_plane *plane; | 
| +    struct weston_drm_format *fmt; | 
| + | 
| +    output = zalloc(sizeof *output); | 
| +    if (!output) | 
| +        return NULL; | 
| + | 
| +    weston_output_init(&output->base, b->compositor, "DUMMY"); | 
| + | 
| +    output->base.enable = drm_dummy_output_enable; | 
| +    output->base.destroy = drm_dummy_output_destroy; | 
| +    output->base.disable = drm_dummy_output_disable; | 
| + | 
| +    output->base.start_repaint_loop = drm_dummy_output_start_repaint_loop; | 
| +    output->base.repaint = drm_dummy_output_repaint; | 
| +    output->base.set_dpms = NULL; | 
| +    output->base.switch_mode = NULL; | 
| +    output->base.gamma_size = 0; | 
| +    output->base.set_gamma = NULL; | 
| +    output->base.unavailable = true; | 
| + | 
| +    weston_compositor_add_pending_output(&output->base, b->compositor); | 
| + | 
| +#ifdef BUILD_DRM_GBM | 
| +    output->gbm_bo_flags = GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING; | 
| +    output->gbm_format = DRM_FORMAT_XRGB8888; | 
| +#endif | 
| + | 
| +    plane = zalloc(sizeof(*plane)); | 
| +    if (!plane) { | 
| +        weston_output_release(&output->base); | 
| +        free(output); | 
| +        return NULL; | 
| +    } | 
| + | 
| +    plane->type = WDRM_PLANE_TYPE_PRIMARY; | 
| +    plane->backend = b; | 
| +    plane->state_cur = drm_plane_state_alloc(NULL, plane); | 
| +    plane->state_cur->complete = true; | 
| + | 
| +    weston_drm_format_array_init(&plane->formats); | 
| +    fmt = weston_drm_format_array_add_format(&plane->formats, | 
| +                         output->gbm_format); | 
| +    weston_drm_format_add_modifier(fmt, DRM_FORMAT_MOD_LINEAR); | 
| + | 
| +    weston_plane_init(&plane->base, b->compositor, 0, 0); | 
| +    wl_list_insert(&b->plane_list, &plane->link); | 
| + | 
| +    output->scanout_plane = plane; | 
| + | 
| +    return &output->base; | 
| +} | 
| + | 
| +static int drm_backend_init_dummy(struct drm_backend *b) | 
| +{ | 
| +    struct weston_mode *mode; | 
| + | 
| +    b->dummy_output = drm_dummy_output_create(b); | 
| +    if (!b->dummy_output) | 
| +        return -1; | 
| + | 
| +    mode = zalloc(sizeof *mode); | 
| +    if (!mode) | 
| +        goto err; | 
| + | 
| +    mode->flags = WL_OUTPUT_MODE_CURRENT; | 
| +    mode->width = 1920; | 
| +    mode->height = 1080; | 
| +    mode->refresh = 60 * 1000LL; | 
| + | 
| +    wl_list_insert(b->dummy_output->mode_list.prev, &mode->link); | 
| + | 
| +    b->dummy_output->current_mode = mode; | 
| + | 
| +    weston_output_set_scale(b->dummy_output, 1); | 
| +    weston_output_set_transform(b->dummy_output, | 
| +                    WL_OUTPUT_TRANSFORM_NORMAL); | 
| + | 
| +    b->dummy_head = zalloc(sizeof *b->dummy_head); | 
| +    if (!b->dummy_head) | 
| +        goto err; | 
| + | 
| +    weston_head_init(&b->dummy_head->base, "DUMMY"); | 
| +    weston_head_set_monitor_strings(&b->dummy_head->base, | 
| +                    "DUMMY", "DUMMY", "DUMMY"); | 
| +    weston_compositor_add_head(b->compositor, &b->dummy_head->base); | 
| +    weston_output_attach_head(b->dummy_output, &b->dummy_head->base); | 
| + | 
| +    return 0; | 
| +err: | 
| +    drm_dummy_output_destroy(b->dummy_output); | 
| +    b->dummy_output = NULL; | 
| +    return -1; | 
| +} | 
| + | 
|  enum drm_head_mode { | 
|      DRM_HEAD_MODE_DEFAULT, | 
|      DRM_HEAD_MODE_PRIMARY, | 
| @@ -4007,6 +4201,11 @@ drm_backend_create(struct weston_compositor *compositor, | 
|          goto err_sprite; | 
|      } | 
|   | 
| +    if (drm_backend_init_dummy(b) < 0) { | 
| +        weston_log("Failed to init dummy output\n"); | 
| +        goto err_udev_input; | 
| +    } | 
| + | 
|      wl_list_init(&b->writeback_connector_list); | 
|      if (drm_backend_update_connectors(b, drm_device) < 0) { | 
|          weston_log("Failed to create heads for %s\n", b->drm.filename); | 
| diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c | 
| index 4b5ba42..a3c55bd 100644 | 
| --- a/libweston/backend-drm/kms.c | 
| +++ b/libweston/backend-drm/kms.c | 
| @@ -1170,6 +1170,9 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, | 
|                  continue; | 
|   | 
|              head = to_drm_head(head_base); | 
| +            if (head == b->dummy_head) | 
| +                continue; | 
| + | 
|              connector_id = head->connector.connector_id; | 
|   | 
|              drm_debug(b, "\t\t[atomic] disabling inactive head %s\n", | 
| diff --git a/libweston/compositor.c b/libweston/compositor.c | 
| index 121871c..67766df 100644 | 
| --- a/libweston/compositor.c | 
| +++ b/libweston/compositor.c | 
| @@ -6436,6 +6436,9 @@ weston_output_set_color_transforms(struct weston_output *output) | 
|      struct weston_color_transform *sRGB_to_blend = NULL; | 
|      bool ok; | 
|   | 
| +    if (!cm) | 
| +        return false; | 
| + | 
|      ok = cm->get_output_color_transform(cm, output, &blend_to_output); | 
|      ok = ok && cm->get_sRGB_to_output_color_transform(cm, output, | 
|                                &sRGB_to_output); | 
| --  | 
| 2.20.1 |