From 6314c37b800c57d162998d5f8197ddfb77f77d87 Mon Sep 17 00:00:00 2001 From: "jh13.son" Date: Fri, 22 May 2015 20:07:33 +0900 Subject: [PATCH] drm backend implementation - output implementation - initial version - create framebuffers - draw using gl-renderer or pixman-renderer - add test code Change-Id: I136872213c54ef887c3a58cbccad95b7f01d1801 --- src/Makefile.am | 3 +- src/compositor.c | 1 + src/modules/drm/drm-common.c | 45 +- src/modules/drm/drm-internal.h | 80 +++ src/modules/drm/drm-output.c | 947 +++++++++++++++++++++++++++++++++ src/modules/libinput/libinput.c | 20 +- src/modules/libinput/pepper-libinput.h | 3 +- src/output.c | 17 +- src/pepper-internal.h | 2 + test/Makefile.am | 7 +- test/drm-backend.c | 56 ++ 11 files changed, 1159 insertions(+), 22 deletions(-) create mode 100644 src/modules/drm/drm-output.c create mode 100644 test/drm-backend.c diff --git a/src/Makefile.am b/src/Makefile.am index df69f96..cc9e513 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -62,7 +62,8 @@ libpepper_la_CFLAGS += $(DRM_BACKEND_CFLAGS) -I$(top_srcdir)/src/modules/libinpu libpepper_la_LIBADD += $(DRM_BACKEND_LIBS) libpepper_la_SOURCES += modules/drm/drm-internal.h \ - modules/drm/drm-common.c + modules/drm/drm-common.c \ + modules/drm/drm-output.c endif # libinput module for drm & fbdev backends diff --git a/src/compositor.c b/src/compositor.c index 594c6bd..74ae8ef 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -75,6 +75,7 @@ pepper_compositor_create(const char *socket_name) compositor_bind); wl_list_init(&compositor->surfaces); wl_list_init(&compositor->seat_list); + wl_list_init(&compositor->output_list); if (wl_display_init_shm(compositor->display) != 0) { diff --git a/src/modules/drm/drm-common.c b/src/modules/drm/drm-common.c index 47f90f1..5e43e15 100644 --- a/src/modules/drm/drm-common.c +++ b/src/modules/drm/drm-common.c @@ -1,3 +1,5 @@ +#include +#include #include "drm-internal.h" PEPPER_API pepper_drm_t * @@ -12,8 +14,15 @@ pepper_drm_create(pepper_compositor_t *compositor, const char *device) goto error; } + drm->udev = udev_new(); + if (!drm->udev) + { + PEPPER_ERROR("Failed to create udev context in %s\n", __FUNCTION__); + goto error; + } + drm->compositor = compositor; - drm->input = pepper_libinput_create(compositor); + drm->input = pepper_libinput_create(compositor, drm->udev); if (!drm->input) { @@ -21,13 +30,19 @@ pepper_drm_create(pepper_compositor_t *compositor, const char *device) goto error; } - /* TODO */ + wl_list_init(&drm->output_list); + + if (!pepper_drm_output_create(drm)) + { + PEPPER_ERROR("Failed to connect drm output in %s\n", __FUNCTION__); + goto error; + } return drm; error: if (drm) - pepper_free(drm); + pepper_drm_destroy(drm); return NULL; } @@ -35,9 +50,29 @@ error: PEPPER_API void pepper_drm_destroy(pepper_drm_t *drm) { - /* TODO */ + drm_output_t *output, *next; + + if (drm->drm_event_source) + wl_event_source_remove(drm->drm_event_source); + + if (!wl_list_empty(&drm->output_list)) + { + wl_list_for_each_safe(output, next, &drm->output_list, link) + pepper_drm_output_destroy(output); + } + + if (drm->crtcs) + pepper_free(drm->crtcs); + + if (drm->drm_fd) + close(drm->drm_fd); + + if (drm->input) + pepper_libinput_destroy(drm->input); + + if (drm->udev) + udev_unref(drm->udev); - pepper_libinput_destroy(drm->input); pepper_free(drm); return; diff --git a/src/modules/drm/drm-internal.h b/src/modules/drm/drm-internal.h index 1ec9ab2..ef64fb4 100644 --- a/src/modules/drm/drm-internal.h +++ b/src/modules/drm/drm-internal.h @@ -1,15 +1,95 @@ #ifndef DRM_INTERNAL_H #define DRM_INTERNAL_H +#include +#include +#include + #include #include #include "pepper-drm.h" +#define DUMB_FB_COUNT 2 + +typedef struct drm_output drm_output_t; +typedef struct drm_fb drm_fb_t; + struct pepper_drm { pepper_compositor_t *compositor; pepper_libinput_t *input; + struct udev *udev; + + int drm_fd; + struct wl_list output_list; + uint32_t *crtcs; + uint32_t count_crtcs; + uint32_t min_width, min_height; + uint32_t max_width, max_height; + + struct wl_event_source *drm_event_source; }; +struct drm_output +{ + pepper_drm_t *drm; + pepper_output_t *base; + + struct wl_list link; + + int32_t subpixel; + uint32_t w, h; + + uint32_t crtc_id; + uint32_t conn_id; + + struct wl_signal destroy_signal; + struct wl_signal mode_change_signal; + struct wl_signal frame_signal; + + int mode_count; + drmModeModeInfo *modes; + drmModeModeInfo *current_mode; + + drmModeCrtc *saved_crtc; + + drm_fb_t *dumb_fb[DUMB_FB_COUNT]; + pixman_image_t *dumb_image[DUMB_FB_COUNT]; + int back_fb_index; + + struct gbm_device *gbm_device; + struct gbm_surface *gbm_surface; + + drm_fb_t *front_fb; + drm_fb_t *back_fb; + + pepper_renderer_t *renderer; + + pepper_bool_t vblank_pending; + pepper_bool_t page_flip_pending; + + /* TODO */ +}; + +struct drm_fb +{ + drm_output_t *output; + + int fd; + uint32_t id; + uint32_t handle; + uint32_t stride; + uint32_t size; + + struct gbm_bo *bo; + void *map; +}; + +pepper_bool_t +pepper_drm_output_create(pepper_drm_t *drm); + +void +pepper_drm_output_destroy(drm_output_t *output); + #endif /* DRM_INTERNAL_H */ diff --git a/src/modules/drm/drm-output.c b/src/modules/drm/drm-output.c new file mode 100644 index 0000000..31043b4 --- /dev/null +++ b/src/modules/drm/drm-output.c @@ -0,0 +1,947 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "drm-internal.h" + +#include +#include + +#define USE_PIXMAN 0 /* FIXME */ + +static pepper_bool_t +init_renderer(drm_output_t *output); + +static void +fini_renderer(drm_output_t *output); + +void +drm_output_destroy(void *o) +{ + drm_output_t *output = (drm_output_t *)o; + + wl_list_remove(&output->link); + + if (output->saved_crtc) + { + drmModeCrtc *c = output->saved_crtc; + drmModeSetCrtc(output->drm->drm_fd, c->crtc_id, c->buffer_id, c->x, c->y, + &output->conn_id, 1, &c->mode); + } + + fini_renderer(output); + + if (output->modes) + pepper_free(output->modes); + + pepper_free(output); +} + +static void +drm_output_add_destroy_listener(void *o, struct wl_listener *listener) +{ + drm_output_t *output = (drm_output_t *)o; + wl_signal_add(&output->destroy_signal, listener); +} + +static void +drm_output_add_mode_change_listener(void *o, struct wl_listener *listener) +{ + drm_output_t *output = (drm_output_t *)o; + wl_signal_add(&output->mode_change_signal, listener); +} + +static int32_t +drm_output_get_subpixel_order(void *data) +{ + drm_output_t *output = (drm_output_t *)data; + + switch (output->subpixel) + { + case DRM_MODE_SUBPIXEL_UNKNOWN: + return WL_OUTPUT_SUBPIXEL_UNKNOWN; + case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB: + return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB; + case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR: + return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR; + case DRM_MODE_SUBPIXEL_VERTICAL_RGB: + return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB; + case DRM_MODE_SUBPIXEL_VERTICAL_BGR: + return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR; + case DRM_MODE_SUBPIXEL_NONE: + return WL_OUTPUT_SUBPIXEL_NONE; + default: + return WL_OUTPUT_SUBPIXEL_UNKNOWN; + } + + return WL_OUTPUT_SUBPIXEL_UNKNOWN; +} + +static const char * +drm_output_get_maker_name(void *output) +{ + PEPPER_IGNORE(output); + return "PePPer DRM"; +} + +static const char * +drm_output_get_model_name(void *output) +{ + PEPPER_IGNORE(output); + return "PePPer DRM"; +} + +static int +drm_output_get_mode_count(void *o) +{ + drm_output_t *output = (drm_output_t *)o; + drmModeConnector *c = drmModeGetConnector(output->drm->drm_fd, output->conn_id); + int count = c->count_modes; + + drmModeFreeConnector(c); + + return count; +} + +static void +drm_output_get_mode(void *o, int index, pepper_output_mode_t *mode) +{ + drm_output_t *output = (drm_output_t *)o; + drmModeModeInfo *m = &(output->modes[index]); + + mode->flags = 0; + mode->w = m->hdisplay; + mode->h = m->vdisplay; + mode->refresh = 60000/* FIXME */; + + if (m->type & DRM_MODE_TYPE_PREFERRED) + mode->flags |= WL_OUTPUT_MODE_PREFERRED; + + if (m == output->current_mode) + mode->flags |= WL_OUTPUT_MODE_CURRENT; +} + +static pepper_bool_t +drm_output_set_mode(void *o, const pepper_output_mode_t *mode) +{ + int i; + drm_output_t *output = (drm_output_t *)o; + + /* + * typedef struct _drmModeModeInfo { + * uint32_t clock; + * uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew; + * uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan; + * + * uint32_t vrefresh; + * + * uint32_t flags; + * uint32_t type; + * char name[DRM_DISPLAY_MODE_LEN]; + * } drmModeModeInfo, *drmModeModeInfoPtr; + */ + for (i = 0; i < output->mode_count; i++) + { + drmModeModeInfo *m = &(output->modes[i]); + if ((m->hdisplay == mode->w) && (m->vdisplay == mode->h)) /* FIXME */ + { + output->current_mode = m; + output->w = m->hdisplay; + output->h = m->vdisplay; + + fini_renderer(output); + init_renderer(output); + + wl_signal_emit(&output->mode_change_signal, output); + + return PEPPER_TRUE; + } + } + + return PEPPER_FALSE; +} + +static void +destroy_fb(struct gbm_bo *bo, void *data) +{ + drm_fb_t *fb = (drm_fb_t *)data; + + if (fb->id) + drmModeRmFB(fb->fd, fb->id); + + pepper_free(fb); +} + +static drm_fb_t * +create_fb(drm_output_t *output, struct gbm_bo *bo) +{ + int ret; + uint32_t w, h; + drm_fb_t *fb; + + fb = (drm_fb_t *)pepper_calloc(1, sizeof(drm_fb_t)); + if (!fb) + { + PEPPER_ERROR("Failed to allocate memory in %s\n", __FUNCTION__); + goto error; + } + + w = gbm_bo_get_width(bo); + h = gbm_bo_get_height(bo); + fb->fd = output->drm->drm_fd; + fb->handle = gbm_bo_get_handle(bo).u32; + fb->stride = gbm_bo_get_stride(bo); + fb->size = fb->stride * h; + fb->bo = bo; + + ret = drmModeAddFB(fb->fd, w, h, 24, 32, fb->stride, fb->handle, &fb->id); + if (ret) + { + PEPPER_ERROR("Failed to add fb in %s\n", __FUNCTION__); + goto error; + } + + gbm_bo_set_user_data(bo, fb, destroy_fb); + + return fb; + +error: + + if (fb) + destroy_fb(bo, fb); + + return NULL; +} + +static drm_fb_t * +get_fb(drm_output_t *output, struct gbm_bo *bo) +{ + drm_fb_t *fb = (drm_fb_t *)gbm_bo_get_user_data(bo); + + if (fb) + return fb; + + return create_fb(output, bo); +} + +static void +draw_gl(drm_output_t *output) +{ + struct gbm_bo *bo; + + output->renderer->draw(output->renderer, NULL/*FIXME*/, NULL); + + bo = gbm_surface_lock_front_buffer(output->gbm_surface); + if (!bo) + { + PEPPER_ERROR("Failed to lock front buffer in %s\n", __FUNCTION__); + return; + } + + output->back_fb = get_fb(output, bo); + if (!output->back_fb) + { + PEPPER_ERROR("Failed to get back fb in %s\n", __FUNCTION__); + gbm_surface_release_buffer(output->gbm_surface, bo); + return; + } +} + +static void +draw_pixman(drm_output_t *output) +{ + output->back_fb_index ^= 1; + output->back_fb = output->dumb_fb[output->back_fb_index]; + output->renderer->draw(output->renderer, output->dumb_image[output->back_fb_index], NULL); +} + +static void +draw(drm_output_t *output) +{ + if (USE_PIXMAN/* FIXME */) + draw_pixman(output); + else + draw_gl(output); +} + +static void +drm_output_repaint(void *o) +{ + int ret; + drm_output_t *output = (drm_output_t *)o; + + draw(output); + + if (!output->back_fb) + return; + + ret = drmModeSetCrtc(output->drm->drm_fd, output->crtc_id, output->back_fb->id, + 0, 0, &output->conn_id, 1, output->current_mode); + if (ret) + { + PEPPER_ERROR("Failed to set CRTC[%d] for Connector[%d] in %s\n", + output->crtc_id, output->conn_id, __FUNCTION__); + return; + } + + ret = drmModePageFlip(output->drm->drm_fd, output->crtc_id, output->back_fb->id, + DRM_MODE_PAGE_FLIP_EVENT, output); + if (ret < 0) + { + PEPPER_ERROR("Failed to queue pageflip in %s\n", __FUNCTION__); + return; + } + + output->page_flip_pending = PEPPER_TRUE; + + /* TODO: set planes */ + +} + +static void +drm_output_add_frame_listener(void *o, struct wl_listener *listener) +{ + drm_output_t *output = (drm_output_t *)o; + wl_signal_add(&output->frame_signal, listener); +} + +struct pepper_output_interface drm_output_interface = +{ + drm_output_destroy, + drm_output_add_destroy_listener, + drm_output_add_mode_change_listener, + + drm_output_get_subpixel_order, + drm_output_get_maker_name, + drm_output_get_model_name, + + drm_output_get_mode_count, + drm_output_get_mode, + drm_output_set_mode, + + drm_output_repaint, + drm_output_add_frame_listener, +}; + +static struct udev_device * +find_primary_gpu(struct udev *udev) /* FIXME: copied from weston */ +{ + struct udev_enumerate *e; + struct udev_list_entry *entry; + struct udev_device *pci, *device, *drm_device = NULL; + const char *path, *device_seat, *id; + + e = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(e, "drm"); + udev_enumerate_add_match_sysname(e, "card[0-9]*"); + udev_enumerate_scan_devices(e); + + udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) + { + path = udev_list_entry_get_name(entry); + device = udev_device_new_from_syspath(udev, path); + if (!device) + continue; + device_seat = udev_device_get_property_value(device, "ID_SEAT"); + if (!device_seat) + device_seat = "seat0"; + if (strcmp(device_seat, "seat0" /* FIXME: default seat? */)) + { + udev_device_unref(device); + continue; + } + + pci = udev_device_get_parent_with_subsystem_devtype(device, "pci", NULL); + if (pci) + { + id = udev_device_get_sysattr_value(pci, "boot_vga"); + if (id && !strcmp(id, "1")) + { + if (drm_device) + udev_device_unref(drm_device); + drm_device = device; + break; + } + } + + if (!drm_device) + drm_device = device; + else + udev_device_unref(device); + } + + udev_enumerate_unref(e); + return drm_device; +} + +static int +drm_open(const char *path, int flags) +{ + int fd; + struct stat s; + drm_magic_t m; + + fd = open(path, flags | O_CLOEXEC); + if (fd == -1) + { + PEPPER_ERROR("Failed to open file[%s] in %s\n", path, __FUNCTION__); + goto error; + } + + if (fstat(fd, &s) == -1) + { + PEPPER_ERROR("Failed to get file[%s] state in %s\n", path, __FUNCTION__); + goto error; + } + + if (major(s.st_rdev) != 226/*drm major*/) + { + PEPPER_ERROR("File %s is not a drm device file\n", path); + goto error; + } + + if ((drmGetMagic(fd, &m) != 0) || (drmAuthMagic(fd, m) != 0)) + { + PEPPER_ERROR("drm fd is not master\n"); + goto error; + } + + return fd; + +error: + if (fd > 0) + close(fd); + return -1; +} + +static int +find_crtc(pepper_drm_t *drm, drmModeRes *res, drmModeConnector *conn) +{ + unsigned int i, j; + drmModeEncoder *enc; + drm_output_t *output; + + for (i = 0; i < conn->count_encoders; i++) + { + enc = drmModeGetEncoder(drm->drm_fd, conn->encoders[i]); + if (!enc) + continue; + + for (j = 0; j < res->count_crtcs; j++) + { + if (!(enc->possible_crtcs & (1 << j))) + continue; + + wl_list_for_each(output, &drm->output_list, link) + { + if (res->crtcs[j] == output->crtc_id) + continue; + } + + return res->crtcs[j]; + } + } + + return -1; +} + +/* FIXME: copied from weston */ +static drm_fb_t * +create_dumb_fb(drm_output_t *output) +{ + drm_fb_t *fb; + int ret; + + int drm_fd = output->drm->drm_fd; + uint32_t width = output->w; + uint32_t height = output->h; + + struct drm_mode_create_dumb create_arg; + struct drm_mode_destroy_dumb destroy_arg; + struct drm_mode_map_dumb map_arg; + + fb = pepper_calloc(1, sizeof(drm_fb_t)); + if (!fb) + { + PEPPER_ERROR("Failed to allocate memory in %s\n", __FUNCTION__); + return NULL; + } + + memset(&create_arg, 0, sizeof create_arg); + create_arg.bpp = 32; + create_arg.width = width; + create_arg.height = height; + + ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg); + if (ret) + { + PEPPER_ERROR("Failed to create dumb_fb fb with ioctl in %s\n", __FUNCTION__); + goto err_fb; + } + + fb->handle = create_arg.handle; + fb->stride = create_arg.pitch; + fb->size = create_arg.size; + fb->fd = drm_fd; + + ret = drmModeAddFB(drm_fd, width, height, 24, 32, fb->stride, fb->handle, &fb->id); + if (ret) + { + PEPPER_ERROR("Failed to add fb in %s\n", __FUNCTION__); + goto err_bo; + } + + memset(&map_arg, 0, sizeof map_arg); + map_arg.handle = fb->handle; + ret = drmIoctl(fb->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_arg); + if (ret) + { + PEPPER_ERROR("Failed to map dumb_fb fb in %s\n", __FUNCTION__); + goto err_add_fb; + } + + fb->map = mmap(0, fb->size, PROT_WRITE, MAP_SHARED, drm_fd, map_arg.offset); + if (fb->map == MAP_FAILED) + { + PEPPER_ERROR("Failed to map fb in %s\n", __FUNCTION__); + goto err_add_fb; + } + + return fb; + +err_add_fb: + drmModeRmFB(drm_fd, fb->id); + +err_bo: + memset(&destroy_arg, 0, sizeof(destroy_arg)); + destroy_arg.handle = create_arg.handle; + drmIoctl(drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg); + +err_fb: + pepper_free(fb); + return NULL; +} + +/* FIXME: copied from weston */ +static void +destroy_dumb_fb(drm_fb_t *fb) +{ + struct drm_mode_destroy_dumb destroy_arg; + + if (!fb->map) + return; + + if (fb->id) + drmModeRmFB(fb->fd, fb->id); + + munmap(fb->map, fb->size); + + memset(&destroy_arg, 0, sizeof(destroy_arg)); + destroy_arg.handle = fb->handle; + drmIoctl(fb->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg); + + pepper_free(fb); +} + +static void +fini_pixman_renderer(drm_output_t *output) +{ + int i; + + for (i = 0; i < DUMB_FB_COUNT; i++) + { + if (output->dumb_fb[i]) + destroy_dumb_fb(output->dumb_fb[i]); + if (output->dumb_image[i]) + pixman_image_unref(output->dumb_image[i]); + } + + if (output->renderer && output->renderer->destroy) + output->renderer->destroy(output->renderer); +} + +/* FIXME: copied from weston */ +static pepper_bool_t +init_pixman_renderer(drm_output_t *output) +{ + int i; + + for (i = 0; i < DUMB_FB_COUNT; i++) + { + output->dumb_fb[i] = create_dumb_fb(output); + if (!output->dumb_fb[i]) + { + PEPPER_ERROR("Failed to create dumb_fb[%d] in %s\n", i, __FUNCTION__); + goto error; + } + + output->dumb_image[i] = pixman_image_create_bits(PIXMAN_x8r8g8b8, + output->w, output->h, + output->dumb_fb[i]->map, + output->dumb_fb[i]->stride); + if (!output->dumb_image[i]) + { + PEPPER_ERROR("Failed to create (pixman)dumb_image[%d] in %s\n", i, __FUNCTION__); + goto error; + } + } + + output->renderer = pepper_pixman_renderer_create(); + if (!output->renderer) + { + PEPPER_ERROR("Failed to create pixman renderer in %s\n", __FUNCTION__); + goto error; + } + + return PEPPER_TRUE; + +error: + + fini_pixman_renderer(output); + + return PEPPER_FALSE; +} + +static void +fini_gl_renderer(drm_output_t *output) +{ + if (output->renderer) + output->renderer->destroy(output->renderer); + + if (output->gbm_surface) + gbm_surface_destroy(output->gbm_surface); + + if (output->gbm_device) + gbm_device_destroy(output->gbm_device); +} + +static pepper_bool_t +init_gl_renderer(drm_output_t *output) +{ + uint32_t native_visual_id; + + output->gbm_device = gbm_create_device(output->drm->drm_fd); + if (!output->gbm_device) + { + PEPPER_ERROR("Failed to create gbm device in %s\n", __FUNCTION__); + goto error; + } + + output->gbm_surface = gbm_surface_create(output->gbm_device, output->w, output->h, + GBM_FORMAT_XRGB8888/*FIXME*/, + GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING/*FIXME*/); + if (!output->gbm_surface) + { + PEPPER_ERROR("Failed to create gbm surface in %s\n", __FUNCTION__); + goto error; + } + + /* + * PEPPER_API pepper_renderer_t * + * pepper_gl_renderer_create(pepper_compositor_t *compositor, + * void *display, void *window, const char *platform, + * pepper_format_t format, const uint32_t *native_visual_id); + */ + native_visual_id = GBM_FORMAT_XRGB8888; + output->renderer = pepper_gl_renderer_create(output->drm->compositor, + output->gbm_device, output->gbm_surface, "gbm", + PEPPER_FORMAT_ARGB8888/*FIXME*/, + &native_visual_id/*FIXME*/); + if (!output->renderer) + { + PEPPER_ERROR("Failed to create gl renderer in %s\n", __FUNCTION__); + goto error; + } + + return PEPPER_TRUE; + +error: + fini_gl_renderer(output); + + return PEPPER_FALSE; +} + +static pepper_bool_t +init_renderer(drm_output_t *output) +{ + if (USE_PIXMAN/* FIXME */) + return init_pixman_renderer(output); + else + return init_gl_renderer(output); +} + +static void +fini_renderer(drm_output_t *output) +{ + if (USE_PIXMAN/* FIXME */) + fini_pixman_renderer(output); + else + fini_gl_renderer(output); +} + +static drm_output_t * +drm_output_create(pepper_drm_t *drm, struct udev_device *device, + drmModeRes *res, drmModeConnector *conn) +{ + int i; + drm_output_t *output; + + output = (drm_output_t *)pepper_calloc(1, sizeof(drm_output_t)); + if (!output) + { + PEPPER_ERROR("Failed to allocate memory in %s\n", __FUNCTION__); + goto error; + } + + output->drm = drm; + output->subpixel = conn->subpixel; + + wl_signal_init(&output->destroy_signal); + wl_signal_init(&output->mode_change_signal); + wl_signal_init(&output->frame_signal); + + wl_list_insert(&drm->output_list, &output->link); + + /* find crtc + connector */ + output->crtc_id = find_crtc(drm, res, conn); + if (output->crtc_id < 0) + { + PEPPER_ERROR("Failed to find crtc in %s\n", __FUNCTION__); + goto error; + } + output->conn_id = conn->connector_id; + output->saved_crtc = drmModeGetCrtc(drm->drm_fd, output->crtc_id); + + /* set modes */ + output->mode_count = conn->count_modes; + output->modes = (drmModeModeInfo *)pepper_calloc(conn->count_modes, + sizeof(drmModeModeInfo)); + if (!output->modes) + { + PEPPER_ERROR("Failed to allocate memory in %s\n", __FUNCTION__); + goto error; + } + + memcpy(output->modes, conn->modes, conn->count_modes * sizeof(drmModeModeInfo)); + for (i = 0; i < output->mode_count; i++) + { + drmModeModeInfo *m = &(output->modes[i]); + if (m->type & DRM_MODE_TYPE_PREFERRED) + { + output->current_mode = m; + output->w = m->hdisplay; + output->h = m->vdisplay; + } + } + + if (!init_renderer(output)) + { + PEPPER_ERROR("Failed to initialize renderer in %s\n", __FUNCTION__); + goto error; + } + + return output; + +error: + + if (output) + drm_output_destroy(output); + + return NULL; +} + +static pepper_bool_t +drm_add_outputs(pepper_drm_t *drm, struct udev_device *device) +{ + int i; + drmModeRes *res; + drmModeConnector *conn; + drm_output_t *output; + + res = drmModeGetResources(drm->drm_fd); + if (!res) + { + PEPPER_ERROR("Failed to get drm resources in %s\n", __FUNCTION__); + return PEPPER_FALSE; + } + + drm->crtcs = pepper_calloc(res->count_crtcs, sizeof(uint32_t)); + if (!drm->crtcs) + { + PEPPER_ERROR("Failed to allocate memory in %s\n", __FUNCTION__); + return PEPPER_FALSE; + } + drm->count_crtcs = res->count_crtcs; + memcpy(drm->crtcs, res->crtcs, sizeof(uint32_t) * res->count_crtcs); + + drm->min_width = res->min_width; + drm->min_height = res->min_height; + drm->max_width = res->max_width; + drm->max_height = res->max_height; + + for (i = 0; i < res->count_connectors; i++) + { + conn = drmModeGetConnector(drm->drm_fd, res->connectors[i]); + if (!conn) + continue; + + if (conn->connection != DRM_MODE_CONNECTED/* CHECKME */) + { + drmModeFreeConnector(conn); + continue; + } + + output = drm_output_create(drm, device, res, conn); + if (!output) + { + PEPPER_ERROR("Failed to create drm_output in %s\n", __FUNCTION__); + drmModeFreeConnector(conn); + continue; + } + + /* + * PEPPER_API pepper_output_t * + * pepper_compositor_add_output(pepper_compositor_t *compositor, + * const pepper_output_interface_t *interface, void *data) + */ + output->base = pepper_compositor_add_output(output->drm->compositor, + &drm_output_interface, output); + if (!output->base) + { + PEPPER_ERROR("Failed to add output to compositor in %s\n", __FUNCTION__); + drm_output_destroy(output); + drmModeFreeConnector(conn); + continue; + } + + drmModeFreeConnector(conn); + } + + if (wl_list_empty(&drm->output_list)) + { + PEPPER_ERROR("Failed to find active output in %s\n", __FUNCTION__); + drmModeFreeResources(res); + return PEPPER_FALSE; + } + + drmModeFreeResources(res); + + return PEPPER_TRUE; +} + +static void +handle_vblank(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, + void *user_data) +{ + /* TODO */ +} + +void pepper_output_schedule_repaint(pepper_output_t *output); /* TODO: remove */ + +static void +handle_page_flip(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, + void *user_data) +{ + drm_output_t *output = (drm_output_t *)user_data; + + if (output->page_flip_pending) + { + if (output->front_fb && output->front_fb->bo) /* FIXME */ + gbm_surface_release_buffer(output->gbm_surface, output->front_fb->bo); + output->front_fb = output->back_fb; + output->back_fb = NULL; + } + + output->page_flip_pending = PEPPER_FALSE; + if (!output->vblank_pending) + { + wl_signal_emit(&output->frame_signal, output->base); + } + + pepper_output_schedule_repaint(output->base); /* TODO: remove */ +} + +static int +handle_drm_events(int fd, uint32_t mask, void *data) +{ + drmEventContext ctx; + + memset(&ctx, 0, sizeof(drmEventContext)); + ctx.version = DRM_EVENT_CONTEXT_VERSION; + ctx.vblank_handler = handle_vblank; + ctx.page_flip_handler = handle_page_flip; + drmHandleEvent(fd, &ctx); + + return 0; +} + +pepper_bool_t +pepper_drm_output_create(pepper_drm_t *drm) +{ + struct udev_device *drm_device; + const char *filepath; + + struct wl_display *display; + struct wl_event_loop *loop; + + /* drm open */ + drm_device = find_primary_gpu(drm->udev); + if (!drm_device) + { + PEPPER_ERROR("Failed to find primary gpu in %s\n", __FUNCTION__); + goto error; + } + + filepath = udev_device_get_devnode(drm_device); + drm->drm_fd = drm_open(filepath, O_RDWR); + if (drm->drm_fd < 0) + { + PEPPER_ERROR("Failed to open drm[%s] in %s\n", filepath, __FUNCTION__); + goto error; + } + + /* add outputs */ + if (drm_add_outputs(drm, drm_device) == PEPPER_FALSE) + { + PEPPER_ERROR("Failed to add outputs in %s\n", __FUNCTION__); + goto error; + } + + /* add drm fd to main loop */ + display = pepper_compositor_get_display(drm->compositor); + loop = wl_display_get_event_loop(display); + drm->drm_event_source = wl_event_loop_add_fd(loop, drm->drm_fd, WL_EVENT_READABLE, + handle_drm_events, drm); + if (!drm->drm_event_source) + { + PEPPER_ERROR("Failed to add drm fd to main loop in %s\n", __FUNCTION__); + goto error; + } + + udev_device_unref(drm_device); + + return PEPPER_TRUE; + +error: + + if (drm_device) + udev_device_unref(drm_device); + + return PEPPER_FALSE; +} + +void +pepper_drm_output_destroy(drm_output_t *output) +{ + drm_output_destroy(output); +} diff --git a/src/modules/libinput/libinput.c b/src/modules/libinput/libinput.c index ffc7821..611c318 100644 --- a/src/modules/libinput/libinput.c +++ b/src/modules/libinput/libinput.c @@ -477,7 +477,7 @@ handle_libinput_events(int fd, uint32_t mask, void *data) } PEPPER_API pepper_libinput_t * -pepper_libinput_create(pepper_compositor_t *compositor) +pepper_libinput_create(pepper_compositor_t *compositor, struct udev *udev) { struct wl_display *display; struct wl_event_loop *loop; @@ -487,22 +487,18 @@ pepper_libinput_create(pepper_compositor_t *compositor) if (!input) { PEPPER_ERROR("Failed to allocate memory in %s\n", __FUNCTION__); - return PEPPER_FALSE; - } - - input->udev = udev_new(); - if (!input->udev) - { - PEPPER_ERROR("Failed to initialize udev in %s\n", __FUNCTION__); goto error; } + input->udev = udev; + input->libinput = libinput_udev_create_context(&libinput_interface, input, input->udev); if (!input->libinput) { PEPPER_ERROR("Failed to initialize libinput in %s\n", __FUNCTION__); goto error; } + libinput_ref(input->libinput); if (libinput_udev_assign_seat(input->libinput, "seat0"/* FIXME */) != 0) { @@ -546,9 +542,11 @@ pepper_libinput_destroy(pepper_libinput_t *input) wl_list_for_each_safe(seat, tmp, &input->seat_list, link) libinput_seat_destroy(seat); - udev_unref(input->udev); - libinput_unref(input->libinput); - wl_event_source_remove(input->libinput_event_source); + if (input->libinput) + libinput_unref(input->libinput); + + if (input->libinput_event_source) + wl_event_source_remove(input->libinput_event_source); pepper_free(input); } diff --git a/src/modules/libinput/pepper-libinput.h b/src/modules/libinput/pepper-libinput.h index c62af96..0f5151e 100644 --- a/src/modules/libinput/pepper-libinput.h +++ b/src/modules/libinput/pepper-libinput.h @@ -1,6 +1,7 @@ #ifndef PEPPER_LIBINPUT_H #define PEPPER_LIBINPUT_H +#include #include #ifdef __cplusplus @@ -10,7 +11,7 @@ extern "C" { typedef struct pepper_libinput pepper_libinput_t; PEPPER_API pepper_libinput_t * -pepper_libinput_create(pepper_compositor_t *compositor); +pepper_libinput_create(pepper_compositor_t *compositor, struct udev *udev); PEPPER_API void pepper_libinput_destroy(pepper_libinput_t *input); diff --git a/src/output.c b/src/output.c index 00c9c2b..e9da091 100644 --- a/src/output.c +++ b/src/output.c @@ -3,8 +3,9 @@ static void output_update_mode(pepper_output_t *output) { - int i; - struct wl_resource *resource; + int i; + struct wl_resource *resource; + pepper_output_mode_t *preferred_mode = NULL; output->current_mode = NULL; @@ -28,8 +29,13 @@ output_update_mode(pepper_output_t *output) if (output->modes[i].flags & WL_OUTPUT_MODE_CURRENT) output->current_mode = &output->modes[i]; + if (output->modes[i].flags & WL_OUTPUT_MODE_PREFERRED) + preferred_mode = &output->modes[i]; } + if (!output->current_mode) + output->current_mode = preferred_mode; + wl_resource_for_each(resource, &output->resources) { for (i = 0; i < output->mode_count; i++) @@ -211,6 +217,8 @@ pepper_compositor_add_output(pepper_compositor_t *compositor, output->geometry.w = output->current_mode->w; output->geometry.h = output->current_mode->h; + wl_list_insert(&compositor->output_list, &output->link); + /* Install listeners. */ output->data_destroy_listener.notify = handle_output_data_destroy; interface->add_destroy_listener(data, &output->data_destroy_listener); @@ -235,6 +243,8 @@ pepper_output_get_compositor(pepper_output_t *output) PEPPER_API void pepper_output_destroy(pepper_output_t *output) { + wl_list_remove(&output->link); + if (output->interface && output->data) output->interface->destroy(output->data); @@ -292,5 +302,8 @@ pepper_output_get_mode(pepper_output_t *output, int index) PEPPER_API pepper_bool_t pepper_output_set_mode(pepper_output_t *output, const pepper_output_mode_t *mode) { + if (output->current_mode == mode) + return PEPPER_TRUE; + return output->interface->set_mode(output->data, mode); } diff --git a/src/pepper-internal.h b/src/pepper-internal.h index 8504fef..e88cbd6 100644 --- a/src/pepper-internal.h +++ b/src/pepper-internal.h @@ -22,6 +22,7 @@ struct pepper_compositor struct wl_list regions; struct wl_list seat_list; struct wl_list layers; + struct wl_list output_list; }; struct pepper_output @@ -30,6 +31,7 @@ struct pepper_output struct wl_global *global; struct wl_list resources; + struct wl_list link; pepper_output_geometry_t geometry; int32_t scale; diff --git a/test/Makefile.am b/test/Makefile.am index a2e4780..8fdd861 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,4 +1,5 @@ -TESTPROGRAMS = wayland-backend \ +TESTPROGRAMS = drm-backend \ + wayland-backend \ x11-backend \ simple-touch \ simple-shm @@ -7,7 +8,9 @@ AM_CFLAGS = $(TEST_PROGRAM_CFLAGS) \ -I$(top_srcdir)/src/ \ -I$(top_srcdir)/src/modules/wayland/ \ -I$(top_srcdir)/src/modules/x11/ \ - -I$(top_srcdir)/src/modules/drm/ + -I$(top_srcdir)/src/modules/drm/ \ + -I$(top_srcdir)/src/modules/libinput/ \ + -I$(top_srcdir)/src/modules/desktop-shell LDADD = $(top_builddir)/src/libpepper.la $(TEST_PROGRAM_LIBS) diff --git a/test/drm-backend.c b/test/drm-backend.c new file mode 100644 index 0000000..e0b0f62 --- /dev/null +++ b/test/drm-backend.c @@ -0,0 +1,56 @@ +#include +#include +#include + +#include +#include +#include +#include + +static int +handle_sigint(int signal_number, void *data) +{ + struct wl_display *display = (struct wl_display *)data; + wl_display_terminate(display); + + return 0; +} + +int +main(int argc, char **argv) +{ + pepper_compositor_t *compositor; + pepper_drm_t *drm; + struct wl_display *display; + struct wl_event_loop *loop; + struct wl_event_source *sigint; + + { /* for gdb attach */ + char cc; + scanf("%c", &cc); + } + + compositor = pepper_compositor_create("wayland-0"); + PEPPER_ASSERT(compositor); + + drm = pepper_drm_create(compositor, ""); + PEPPER_ASSERT(drm); + + if (!pepper_desktop_shell_init(compositor)) + PEPPER_ASSERT(0); + + display = pepper_compositor_get_display(compositor); + PEPPER_ASSERT(display); + + loop = wl_display_get_event_loop(display); + sigint = wl_event_loop_add_signal(loop, SIGINT, handle_sigint, display); + PEPPER_ASSERT(sigint); + + wl_display_run(display); + + wl_event_source_remove(sigint); + pepper_drm_destroy(drm); + pepper_compositor_destroy(compositor); + + return 0; +} -- 2.7.4