diff --git a/backend/drm/backend.c b/backend/drm/backend.c index 5d72d9dc..42e7f5fd 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -90,7 +90,7 @@ error_backend: static void free_output(void *item) { struct wlr_drm_output *out = item; - wlr_drm_output_free(out, true); + wlr_drm_output_cleanup(out, true); free(out); } diff --git a/backend/drm/drm.c b/backend/drm/drm.c index f636c42c..fc2626c5 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -37,29 +37,6 @@ static const char *conn_name[] = { [DRM_MODE_CONNECTOR_DSI] = "DSI", }; -static void page_flip_handler(int fd, unsigned seq, unsigned tv_sec, unsigned tv_usec, - void *user) { - - struct wlr_drm_output *out = user; - struct wlr_drm_backend *backend = wl_container_of(out->renderer, backend, renderer); - - out->pageflip_pending = true; - if (!out->cleanup) { - wl_signal_emit(&backend->signals.output_render, out); - } -} - -int wlr_drm_event(int fd, uint32_t mask, void *data) { - drmEventContext event = { - .version = DRM_EVENT_CONTEXT_VERSION, - .page_flip_handler = page_flip_handler, - }; - - drmHandleEvent(fd, &event); - - return 1; -} - bool wlr_drm_renderer_init(struct wlr_drm_renderer *renderer, int fd) { renderer->gbm = gbm_create_device(fd); @@ -86,81 +63,6 @@ void wlr_drm_renderer_free(struct wlr_drm_renderer *renderer) { gbm_device_destroy(renderer->gbm); } -static int find_id(const void *item, const void *cmp_to) { - const struct wlr_drm_output *out = item; - const uint32_t *id = cmp_to; - - if (out->connector < *id) { - return -1; - } else if (out->connector > *id) { - return 1; - } else { - return 0; - } -} - -void wlr_drm_scan_connectors(struct wlr_drm_backend *backend) { - drmModeRes *res = drmModeGetResources(backend->fd); - if (!res) { - wlr_log(L_ERROR, "Failed to get DRM resources"); - return; - } - - for (int i = 0; i < res->count_connectors; ++i) { - uint32_t id = res->connectors[i]; - - drmModeConnector *conn = drmModeGetConnector(backend->fd, id); - if (!conn) { - wlr_log(L_ERROR, "Failed to get DRM connector"); - continue; - } - - struct wlr_drm_output *out; - int index = list_seq_find(backend->outputs, find_id, &id); - - if (index == -1) { - out = calloc(1, sizeof *out); - if (!out) { - wlr_log(L_ERROR, "Allocation failed: %s", strerror(errno)); - drmModeFreeConnector(conn); - continue; - } - - out->renderer = &backend->renderer; - out->state = DRM_OUTPUT_DISCONNECTED; - out->connector = res->connectors[i]; - snprintf(out->name, sizeof out->name, "%s-%"PRIu32, - conn_name[conn->connector_type], - conn->connector_type_id); - - list_add(backend->outputs, out); - wlr_log(L_INFO, "Found display '%s'", out->name); - } else { - out = backend->outputs->items[index]; - } - - if (out->state == DRM_OUTPUT_DISCONNECTED && - conn->connection == DRM_MODE_CONNECTED) { - - out->state = DRM_OUTPUT_NEEDS_MODESET; - wlr_log(L_INFO, "Sending modesetting signal for '%s'", out->name); - wl_signal_emit(&backend->signals.output_add, out); - - } else if (out->state == DRM_OUTPUT_CONNECTED && - conn->connection != DRM_MODE_CONNECTED) { - - out->state = DRM_OUTPUT_DISCONNECTED; - wlr_drm_output_free(out, false); - wlr_log(L_INFO, "Sending destruction signal for '%s'", out->name); - wl_signal_emit(&backend->signals.output_rem, out); - } - - drmModeFreeConnector(conn); - } - - drmModeFreeResources(res); -} - static void free_fb(struct gbm_bo *bo, void *data) { uint32_t *id = data; @@ -192,6 +94,25 @@ static uint32_t get_fb_for_bo(int fd, struct gbm_bo *bo) { return *id; } +void wlr_drm_output_begin(struct wlr_drm_output *out) { + struct wlr_drm_renderer *renderer = out->renderer; + eglMakeCurrent(renderer->egl.display, out->egl, out->egl, renderer->egl.context); +} + +void wlr_drm_output_end(struct wlr_drm_output *out) { + struct wlr_drm_renderer *renderer = out->renderer; + eglSwapBuffers(renderer->egl.display, out->egl); + + struct gbm_bo *bo = gbm_surface_lock_front_buffer(out->gbm); + uint32_t fb_id = get_fb_for_bo(renderer->fd, bo); + + drmModePageFlip(renderer->fd, out->crtc, fb_id, DRM_MODE_PAGE_FLIP_EVENT, out); + + gbm_surface_release_buffer(out->gbm, bo); + + out->pageflip_pending = false; +} + static bool display_init_renderer(struct wlr_drm_renderer *renderer, struct wlr_drm_output *out) { @@ -225,7 +146,7 @@ static bool display_init_renderer(struct wlr_drm_renderer *renderer, uint32_t fb_id = get_fb_for_bo(renderer->fd, bo); drmModeSetCrtc(renderer->fd, out->crtc, fb_id, 0, 0, - &out->connector, 1, out->active_mode); + &out->connector, 1, &out->active_mode->mode); drmModePageFlip(renderer->fd, out->crtc, fb_id, DRM_MODE_PAGE_FLIP_EVENT, out); gbm_surface_release_buffer(out->gbm, bo); @@ -233,54 +154,119 @@ static bool display_init_renderer(struct wlr_drm_renderer *renderer, return true; } -static drmModeModeInfo *select_mode(size_t num_modes, drmModeModeInfo modes[static num_modes], - drmModeCrtc *old_crtc, const char *str) { +static int find_id(const void *item, const void *cmp_to) { + const struct wlr_drm_output *out = item; + const uint32_t *id = cmp_to; + + if (out->connector < *id) { + return -1; + } else if (out->connector > *id) { + return 1; + } else { + return 0; + } +} - if (strcmp(str, "preferred") == 0) { - return &modes[0]; +void wlr_drm_scan_connectors(struct wlr_drm_backend *backend) { + drmModeRes *res = drmModeGetResources(backend->fd); + if (!res) { + wlr_log(L_ERROR, "Failed to get DRM resources"); + return; } - if (strcmp(str, "current") == 0) { - if (!old_crtc) { - wlr_log(L_ERROR, "Display does not have currently configured mode"); - return NULL; + for (int i = 0; i < res->count_connectors; ++i) { + uint32_t id = res->connectors[i]; + + drmModeConnector *conn = drmModeGetConnector(backend->fd, id); + if (!conn) { + wlr_log(L_ERROR, "Failed to get DRM connector"); + continue; } - for (size_t i = 0; i < num_modes; ++i) { - if (memcmp(&modes[i], &old_crtc->mode, sizeof modes[0]) == 0) { - return &modes[i]; + struct wlr_drm_output *out; + int index = list_seq_find(backend->outputs, find_id, &id); + + if (index == -1) { + out = calloc(1, sizeof *out); + if (!out) { + wlr_log(L_ERROR, "Allocation failed: %s", strerror(errno)); + drmModeFreeConnector(conn); + continue; } + + out->renderer = &backend->renderer; + out->state = DRM_OUTPUT_DISCONNECTED; + out->connector = res->connectors[i]; + snprintf(out->name, sizeof out->name, "%s-%"PRIu32, + conn_name[conn->connector_type], + conn->connector_type_id); + + list_add(backend->outputs, out); + wlr_log(L_INFO, "Found display '%s'", out->name); + } else { + out = backend->outputs->items[index]; } - // We should never get here - assert(0); - } + if (out->state == DRM_OUTPUT_DISCONNECTED && + conn->connection == DRM_MODE_CONNECTED) { - unsigned width = 0; - unsigned height = 0; - unsigned rate = 0; - int ret; + wlr_log(L_INFO, "'%s' connected", out->name); - if ((ret = sscanf(str, "%ux%u@%u", &width, &height, &rate)) != 2 && ret != 3) { - wlr_log(L_ERROR, "Invalid modesetting string '%s'", str); - return NULL; - } + out->modes = malloc(sizeof(*out->modes) * conn->count_modes); + if (!out->modes) { + wlr_log(L_ERROR, "Allocation failed: %s", strerror(errno)); + goto error; + } + + wlr_log(L_INFO, "Detected modes:"); + + out->num_modes = conn->count_modes; + for (int i = 0; i < conn->count_modes; ++i) { + drmModeModeInfo *mode = &conn->modes[i]; + out->modes[i].width = mode->hdisplay; + out->modes[i].height = mode->vdisplay; + // TODO: Calculate more accurate refresh rate + out->modes[i].rate = mode->vrefresh; + out->modes[i].mode = *mode; + + wlr_log(L_INFO, " %"PRIu16"@%"PRIu16"@%"PRIu32, + mode->hdisplay, mode->vdisplay, + mode->vrefresh); + } - for (size_t i = 0; i < num_modes; ++i) { - if (modes[i].hdisplay == width && modes[i].vdisplay == height && - (!rate || modes[i].vrefresh == rate)) { + out->state = DRM_OUTPUT_NEEDS_MODESET; + wlr_log(L_INFO, "Sending modesetting signal for '%s'", out->name); + wl_signal_emit(&backend->signals.output_add, out); - return &modes[i]; + } else if (out->state == DRM_OUTPUT_CONNECTED && + conn->connection != DRM_MODE_CONNECTED) { + + wlr_drm_output_cleanup(out, false); + wlr_log(L_INFO, "Sending destruction signal for '%s'", out->name); + wl_signal_emit(&backend->signals.output_rem, out); } +error: + drmModeFreeConnector(conn); } - wlr_log(L_ERROR, "Unable to find mode %ux%u@%u", width, height, rate); - return NULL; + drmModeFreeResources(res); +} + +struct wlr_drm_mode *wlr_drm_output_get_modes(struct wlr_drm_output *out, size_t *count) { + if (out->state == DRM_OUTPUT_DISCONNECTED) { + *count = 0; + return NULL; + } + + *count = out->num_modes; + return out->modes; } -bool wlr_drm_output_modeset(struct wlr_drm_output *out, const char *str) { +bool wlr_drm_output_modeset(struct wlr_drm_output *out, struct wlr_drm_mode *mode) { struct wlr_drm_backend *backend = wl_container_of(out->renderer, backend, renderer); - wlr_log(L_INFO, "Modesetting %s with '%s'", out->name, str); + + wlr_log(L_INFO, "Modesetting '%s' with '%ux%u@%u'", out->name, mode->width, + mode->height, mode->rate); drmModeConnector *conn = drmModeGetConnector(backend->fd, out->connector); if (!conn) { @@ -293,34 +279,12 @@ bool wlr_drm_output_modeset(struct wlr_drm_output *out, const char *str) { goto error; } - out->num_modes = conn->count_modes; - out->modes = malloc(sizeof *out->modes * out->num_modes); - if (!out->modes) { - wlr_log(L_ERROR, "Allocation failed: %s", strerror(errno)); - goto error; - } - memcpy(out->modes, conn->modes, sizeof *out->modes * out->num_modes); - - wlr_log(L_INFO, "Detected modes:"); - for (size_t i = 0; i < out->num_modes; ++i) { - wlr_log(L_INFO, " %"PRIu16"@%"PRIu16"@%"PRIu32, - out->modes[i].hdisplay, out->modes[i].vdisplay, - out->modes[i].vrefresh); - } - drmModeEncoder *curr_enc = drmModeGetEncoder(backend->fd, conn->encoder_id); if (curr_enc) { out->old_crtc = drmModeGetCrtc(backend->fd, curr_enc->crtc_id); free(curr_enc); } - out->active_mode = select_mode(out->num_modes, out->modes, - out->old_crtc, str); - if (!out->active_mode) { - wlr_log(L_ERROR, "Failed to configure %s", out->name); - goto error; - } - drmModeRes *res = drmModeGetResources(backend->fd); if (!res) { wlr_log(L_ERROR, "Failed to get DRM resources"); @@ -358,9 +322,9 @@ bool wlr_drm_output_modeset(struct wlr_drm_output *out, const char *str) { } out->state = DRM_OUTPUT_CONNECTED; - - out->width = out->active_mode->hdisplay; - out->height = out->active_mode->vdisplay; + out->active_mode = mode; + out->width = mode->width; + out->height = mode->height; if (!display_init_renderer(&backend->renderer, out)) { wlr_log(L_ERROR, "Failed to initalise renderer for %s", out->name); @@ -369,10 +333,6 @@ bool wlr_drm_output_modeset(struct wlr_drm_output *out, const char *str) { drmModeFreeConnector(conn); - wlr_log(L_INFO, "Configuring %s with mode %"PRIu16"x%"PRIu16"@%"PRIu32"", - out->name, out->active_mode->hdisplay, out->active_mode->vdisplay, - out->active_mode->vrefresh); - return true; error: @@ -385,58 +345,79 @@ error: return false; } -void wlr_drm_output_free(struct wlr_drm_output *out, bool restore) { - if (!out || out->state != DRM_OUTPUT_CONNECTED) { - return; +static void page_flip_handler(int fd, unsigned seq, unsigned tv_sec, unsigned tv_usec, + void *user) { + + struct wlr_drm_output *out = user; + struct wlr_drm_backend *backend = wl_container_of(out->renderer, backend, renderer); + + out->pageflip_pending = true; + if (!out->cleanup) { + wl_signal_emit(&backend->signals.output_render, out); } +} - struct wlr_drm_renderer *renderer = out->renderer; +int wlr_drm_event(int fd, uint32_t mask, void *data) { + drmEventContext event = { + .version = DRM_EVENT_CONTEXT_VERSION, + .page_flip_handler = page_flip_handler, + }; - eglDestroySurface(renderer->egl.display, out->egl); - gbm_surface_destroy(out->gbm); + drmHandleEvent(fd, &event); - free(out->modes); - out->state = DRM_OUTPUT_DISCONNECTED; + return 1; +} - if (!restore) { +static void restore_output(struct wlr_drm_output *out, int fd) { + drmModeCrtc *crtc = out->old_crtc; + if (!crtc) { return; } - drmModeCrtc *crtc = out->old_crtc; - if (crtc) { - // Wait for exising page flips to finish - - drmEventContext event = { - .version = DRM_EVENT_CONTEXT_VERSION, - .page_flip_handler = page_flip_handler, - }; - - out->cleanup = true; - while (out->pageflip_pending) - drmHandleEvent(renderer->fd, &event); - - drmModeSetCrtc(renderer->fd, crtc->crtc_id, crtc->buffer_id, - crtc->x, crtc->y, &out->connector, - 1, &crtc->mode); - drmModeFreeCrtc(crtc); + // Wait for exising page flips to finish + + out->cleanup = true; + while (out->pageflip_pending) { + wlr_drm_event(fd, 0, NULL); } -} -void wlr_drm_output_begin(struct wlr_drm_output *out) { - struct wlr_drm_renderer *renderer = out->renderer; - eglMakeCurrent(renderer->egl.display, out->egl, out->egl, renderer->egl.context); + drmModeSetCrtc(fd, crtc->crtc_id, crtc->buffer_id, crtc->x, crtc->y, + &out->connector, 1, &crtc->mode); + drmModeFreeCrtc(crtc); } -void wlr_drm_output_end(struct wlr_drm_output *out) { - struct wlr_drm_renderer *renderer = out->renderer; - eglSwapBuffers(renderer->egl.display, out->egl); +void wlr_drm_output_cleanup(struct wlr_drm_output *out, bool restore) { + if (!out) { + return; + } - struct gbm_bo *bo = gbm_surface_lock_front_buffer(out->gbm); - uint32_t fb_id = get_fb_for_bo(renderer->fd, bo); + struct wlr_drm_renderer *renderer = out->renderer; - drmModePageFlip(renderer->fd, out->crtc, fb_id, DRM_MODE_PAGE_FLIP_EVENT, out); + switch (out->state) { + case DRM_OUTPUT_CONNECTED: + eglDestroySurface(renderer->egl.display, out->egl); + gbm_surface_destroy(out->gbm); + + out->egl = EGL_NO_SURFACE; + out->gbm = NULL; + /* Fallthrough */ + + case DRM_OUTPUT_NEEDS_MODESET: + free(out->modes); + out->num_modes = 0; + out->modes = NULL; + out->active_mode = NULL; + out->width = 0; + out->height = 0; + + if (restore) { + restore_output(out, renderer->fd); + } - gbm_surface_release_buffer(out->gbm, bo); + out->state = DRM_MODE_DISCONNECTED; + break; - out->pageflip_pending = false; + case DRM_OUTPUT_DISCONNECTED: + break; + } } diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index fb2f3823..29a77675 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -1,3 +1,7 @@ +include_directories( + ${DRM_INCLUDE_DIRS} +) + add_executable(example main.c ) diff --git a/example/example-drm.c b/example/example-drm.c index 35a4f9a2..a2643949 100644 --- a/example/example-drm.c +++ b/example/example-drm.c @@ -23,8 +23,10 @@ void output_add(struct wl_listener *listener, void *data) { struct wlr_drm_output *out = data; - fprintf(stderr, "Output added\n"); - wlr_drm_output_modeset(out, "preferred"); + size_t num_modes; + struct wlr_drm_mode *modes = wlr_drm_output_get_modes(out, &num_modes); + + wlr_drm_output_modeset(out, &modes[0]); } void output_rem(struct wl_listener *listener, void *data) @@ -115,4 +117,5 @@ int main() wl_event_source_remove(timer); wlr_drm_backend_free(wlr); + wl_display_destroy(display); } diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 3373f024..daafc144 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -25,7 +25,6 @@ bool wlr_drm_renderer_init(struct wlr_drm_renderer *renderer, int fd); void wlr_drm_renderer_free(struct wlr_drm_renderer *renderer); enum wlr_drm_output_state { - DRM_OUTPUT_INVALID, DRM_OUTPUT_DISCONNECTED, DRM_OUTPUT_NEEDS_MODESET, DRM_OUTPUT_CONNECTED, @@ -37,8 +36,8 @@ struct wlr_drm_output { char name[16]; size_t num_modes; - drmModeModeInfo *modes; - drmModeModeInfo *active_mode; + struct wlr_drm_mode *modes; + struct wlr_drm_mode *active_mode; uint32_t width; uint32_t height; @@ -54,11 +53,7 @@ struct wlr_drm_output { bool cleanup; }; -bool wlr_drm_output_modeset(struct wlr_drm_output *out, const char *str); -void wlr_drm_output_free(struct wlr_drm_output *out, bool restore); - -void wlr_drm_output_begin(struct wlr_drm_output *out); -void wlr_drm_output_end(struct wlr_drm_output *out); +void wlr_drm_output_cleanup(struct wlr_drm_output *out, bool restore); void wlr_drm_scan_connectors(struct wlr_drm_backend *backend); int wlr_drm_event(int fd, uint32_t mask, void *data); diff --git a/include/wlr/backend/drm.h b/include/wlr/backend/drm.h index d56c7e77..c3df2409 100644 --- a/include/wlr/backend/drm.h +++ b/include/wlr/backend/drm.h @@ -3,16 +3,26 @@ #include #include +#include // drmModeModeInfo struct wlr_drm_backend; struct wlr_drm_output; +struct wlr_drm_mode { + uint16_t width; + uint16_t height; + uint32_t rate; + drmModeModeInfo mode; +}; + struct wlr_drm_backend *wlr_drm_backend_init(struct wl_display *display, struct wlr_session *session, struct wl_listener *add, struct wl_listener *rem, struct wl_listener *render); void wlr_drm_backend_free(struct wlr_drm_backend *backend); -bool wlr_drm_output_modeset(struct wlr_drm_output *out, const char *str); +struct wlr_drm_mode *wlr_drm_output_get_modes(struct wlr_drm_output *out, size_t *count); +bool wlr_drm_output_modeset(struct wlr_drm_output *out, struct wlr_drm_mode *mode); + void wlr_drm_output_begin(struct wlr_drm_output *out); void wlr_drm_output_end(struct wlr_drm_output *out);