swc

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.nihaljere.xyz/swc
Log | Files | Refs | README | LICENSE

commit 170445e630a22a6a38e16f404948dae66660834d
parent 300de285384cdf277c25941854807c449d416375
Author: Michael Forney <mforney@mforney.org>
Date:   Thu, 16 Jan 2014 17:46:12 -0800

Refactor planes

Diffstat:
Mlibswc/compositor.c | 305++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Mlibswc/compositor.h | 2--
Alibswc/cursor_plane.c | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alibswc/cursor_plane.h | 41+++++++++++++++++++++++++++++++++++++++++
Mlibswc/drm.c | 17+++++------------
Mlibswc/drm.h | 11++---------
Alibswc/framebuffer_plane.c | 208+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alibswc/framebuffer_plane.h | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlibswc/local.mk | 3++-
Mlibswc/mode.c | 7+++++++
Mlibswc/mode.h | 2++
Mlibswc/output.c | 41+++++------------------------------------
Mlibswc/output.h | 17+++--------------
Dlibswc/plane.c | 224-------------------------------------------------------------------------------
Dlibswc/plane.h | 66------------------------------------------------------------------
Mlibswc/screen.c | 21+++++++++++++++++++++
Mlibswc/screen.h | 8++++++++
17 files changed, 664 insertions(+), 468 deletions(-)

diff --git a/libswc/compositor.c b/libswc/compositor.c @@ -20,6 +20,15 @@ #include <wld/drm.h> #include <xkbcommon/xkbcommon-keysyms.h> +struct screen +{ + struct wld_surface * surface; + struct wld_buffer * next_buffer, * current_buffer; + struct swc_view * view; + struct wl_listener view_listener; + struct wl_listener event_listener; +}; + struct view { struct swc_view base; @@ -44,12 +53,156 @@ struct view struct wl_list link; }; +static void perform_update(void * data); + +static void buffer_destroy(void * data) +{ + struct swc_buffer * buffer = data; + + swc_buffer_finalize(buffer); + free(buffer); +} + +struct swc_buffer * buffer_get(struct wld_buffer * wld) +{ + if (wld->destroy_data) + return wld->data; + + struct swc_buffer * buffer; + + if (!(buffer = malloc(sizeof *buffer))) + goto error0; + + if (!swc_buffer_initialize(buffer, wld)) + goto error1; + + wld->data = buffer; + wld->destroy_data = &buffer_destroy; + + return buffer; + +error1: + free(buffer); +error0: + return NULL; +} + +static void handle_screen_event(struct wl_listener * listener, void * data) +{ + struct swc_event * event = data; + + if (event->type == SWC_SCREEN_DESTROYED) + { + struct screen * screen + = CONTAINER_OF(listener, typeof(*screen), event_listener); + + wld_destroy_surface(screen->surface); + free(screen); + } +} + +static struct screen * screen_get(struct swc_screen * base) +{ + struct wl_listener * listener + = wl_signal_get(&base->event_signal, &handle_screen_event); + struct screen * screen; + + return listener ? CONTAINER_OF(listener, typeof(*screen), event_listener) + : NULL; +} + +static void handle_screen_view_event(struct wl_listener * listener, void * data) +{ + struct swc_event * event = data; + struct swc_view_event_data * event_data = event->data; + + switch (event->type) + { + case SWC_VIEW_EVENT_FRAME: + { + struct swc_screen_internal * base = CONTAINER_OF + (event_data->view, typeof(*base), planes.framebuffer.view); + struct screen * screen; + struct view * view; + + if (!(screen = screen_get(&base->base))) + return; + + swc.compositor->pending_flips &= ~swc_screen_mask(base); + + if (swc.compositor->pending_flips == 0) + { + wl_list_for_each(view, &swc.compositor->views, link) + swc_view_frame(&view->base, event_data->frame.time); + } + + if (screen->current_buffer) + wld_surface_release(screen->surface, screen->current_buffer); + + screen->current_buffer = screen->next_buffer; + + /* If we had scheduled updates that couldn't run because we were + * waiting on a page flip, run them now. */ + if (swc.compositor->scheduled_updates) + perform_update(swc.compositor); + break; + } + } +} + +static bool screen_swap_buffers(struct screen * screen) +{ + struct swc_buffer * buffer; + + screen->next_buffer = wld_surface_take(screen->surface); + buffer = buffer_get(screen->next_buffer); + + if (!swc_view_attach(screen->view, buffer)) + { + ERROR("Failed to attach next frame to screen\n"); + return false; + } + + return true; +} + +static struct screen * screen_new(struct swc_screen_internal * base) +{ + struct screen * screen; + + if (!(screen = malloc(sizeof *screen))) + goto error0; + + screen->surface = wld_create_surface(swc.drm->context, + base->base.geometry.width, + base->base.geometry.height, + WLD_FORMAT_XRGB8888); + + if (!screen->surface) + goto error1; + + screen->view = &base->planes.framebuffer.view; + screen->view_listener.notify = &handle_screen_view_event; + wl_signal_add(&screen->view->event_signal, &screen->view_listener); + screen->event_listener.notify = &handle_screen_event; + wl_signal_add(&base->base.event_signal, &screen->event_listener); + screen->current_buffer = NULL; + screen_swap_buffers(screen); + + return screen; + +error1: + free(screen); +error0: + return NULL; +} + /* Rendering {{{ */ struct render_target { - struct wld_buffer * buffer; - struct swc_rectangle geometry; + struct wld_surface * surface; + const struct swc_rectangle * geometry; uint32_t mask; }; @@ -78,8 +231,8 @@ static void repaint_view(struct render_target * target, struct view * view, { pixman_region32_translate(&view_damage, -geometry->x, -geometry->y); wld_copy_region(swc.drm->renderer, view->base.buffer->wld, - geometry->x - target->geometry.x, - geometry->y - target->geometry.y, &view_damage); + geometry->x - target->geometry->x, + geometry->y - target->geometry->y, &view_damage); } pixman_region32_fini(&view_damage); @@ -90,7 +243,7 @@ static void repaint_view(struct render_target * target, struct view * view, DEBUG("\t\tRedrawing border\n"); pixman_region32_translate(&border_damage, - -target->geometry.x, -target->geometry.y); + -target->geometry->x, -target->geometry->y); wld_fill_region(swc.drm->renderer, view->border.color, &border_damage); } @@ -105,14 +258,16 @@ static void renderer_repaint(struct render_target * target, struct view * view; DEBUG("Rendering to target { x: %d, y: %d, w: %u, h: %u }\n", - target->geometry.x, target->geometry.y, - target->geometry.width, target->geometry.height); + target->geometry->x, target->geometry->y, + target->geometry->width, target->geometry->height); + + wld_set_target_surface(swc.drm->renderer, target->surface); /* Paint base damage black. */ if (pixman_region32_not_empty(base_damage)) { pixman_region32_translate(base_damage, - -target->geometry.x, -target->geometry.y); + -target->geometry->x, -target->geometry->y); wld_fill_region(swc.drm->renderer, 0xff000000, base_damage); } @@ -445,65 +600,53 @@ static void calculate_damage(struct swc_compositor * compositor) pixman_region32_fini(&surface_opaque); } -static void repaint_screen(struct swc_compositor * compositor, - struct swc_screen_internal * screen, - pixman_region32_t * damage) +static void update_screen(struct swc_compositor * compositor, + struct swc_screen_internal * base) { - struct swc_output * output - = CONTAINER_OF(screen->outputs.next, typeof(*output), link); - pixman_region32_t base_damage; - struct render_target target; - - pixman_region32_init(&base_damage); - pixman_region32_subtract(&base_damage, damage, &compositor->opaque); - - target.buffer = swc_plane_get_buffer(&output->framebuffer_plane); - target.geometry.x = screen->base.geometry.x + output->framebuffer_plane.x; - target.geometry.y = screen->base.geometry.y + output->framebuffer_plane.y; - target.geometry.width = target.buffer->width; - target.geometry.height = target.buffer->height; - target.mask = swc_screen_mask(screen); + struct screen * screen; + const struct swc_rectangle * geometry = &base->base.geometry; + pixman_region32_t damage; - renderer_repaint(&target, damage, &base_damage, &compositor->views); + if (!(compositor->scheduled_updates & swc_screen_mask(base))) + return; - pixman_region32_fini(&base_damage); + if (!(screen = screen_get(&base->base))) + return; - if (!swc_plane_flip(&output->framebuffer_plane)) - fprintf(stderr, "Plane flip failed\n"); -} + pixman_region32_init(&damage); + pixman_region32_intersect_rect(&damage, &compositor->damage, + geometry->x, geometry->y, + geometry->width, geometry->height); + pixman_region32_translate(&damage, -geometry->x, -geometry->y); + pixman_region32_union(&screen->next_buffer->damage, + &screen->next_buffer->damage, &damage); + + /* Don't repaint the screen if it is waiting for a page flip. */ + if (compositor->pending_flips & swc_screen_mask(base)) + return; -static void update_output_damage(struct swc_output * output, - pixman_region32_t * damage) -{ - struct swc_rectangle * geometry = &output->screen->base.geometry; + struct render_target target; + pixman_region32_t * total_damage, base_damage; - pixman_region32_union - (&output->current_damage, &output->current_damage, damage); - pixman_region32_intersect_rect - (&output->current_damage, &output->current_damage, - geometry->x, geometry->y, geometry->width, geometry->height); -} + total_damage = wld_surface_damage(screen->surface, + &screen->next_buffer->damage); + pixman_region32_translate(total_damage, geometry->x, geometry->y); + pixman_region32_init(&base_damage); + pixman_region32_subtract(&base_damage, total_damage, &compositor->opaque); -static void flush_output_damage(struct swc_output * output, - pixman_region32_t * damage) -{ - /* The total damage is composed of the damage from the new frame, and the - * damage from the last frame. */ - pixman_region32_union(damage, - &output->current_damage, &output->previous_damage); + target.surface = screen->surface; + target.mask = swc_screen_mask(base); + target.geometry = geometry; - /* We must save the damage from the previous frame because the back buffer - * is also damaged in this region. */ - pixman_region32_copy(&output->previous_damage, &output->current_damage); - pixman_region32_clear(&output->current_damage); + renderer_repaint(&target, total_damage, &base_damage, &compositor->views); + pixman_region32_fini(&base_damage); + screen_swap_buffers(screen); } static void perform_update(void * data) { struct swc_compositor * compositor = data; struct swc_screen_internal * screen; - struct swc_output * output; - pixman_region32_t damage; uint32_t updates = compositor->scheduled_updates & ~compositor->pending_flips; @@ -513,25 +656,10 @@ static void perform_update(void * data) DEBUG("Performing update\n"); calculate_damage(compositor); - pixman_region32_init(&damage); wl_list_for_each(screen, &swc.screens, link) - { - if (!(compositor->scheduled_updates & swc_screen_mask(screen))) - continue; + update_screen(compositor, screen); - output = CONTAINER_OF(screen->outputs.next, typeof(*output), link); - update_output_damage(output, &compositor->damage); - - /* Don't repaint the output if it is waiting for a page flip. */ - if (compositor->pending_flips & swc_screen_mask(screen)) - continue; - - flush_output_damage(output, &damage); - repaint_screen(compositor, screen, &damage); - } - - pixman_region32_fini(&damage); /* XXX: Should assert that all damage was covered by some output */ pixman_region32_clear(&compositor->damage); compositor->pending_flips |= updates; @@ -568,38 +696,6 @@ static bool handle_motion(struct swc_pointer * pointer, uint32_t time) return false; } -static void handle_drm_event(struct wl_listener * listener, void * data) -{ - struct swc_event * event = data; - struct swc_compositor * compositor; - - compositor = CONTAINER_OF(listener, typeof(*compositor), drm_listener); - - switch (event->type) - { - case SWC_DRM_PAGE_FLIP: - { - struct swc_drm_event_data * event_data = event->data; - struct view * view; - - compositor->pending_flips &= ~SWC_OUTPUT_MASK(event_data->output); - - if (compositor->pending_flips == 0) - { - wl_list_for_each(view, &compositor->views, link) - swc_view_frame(&view->base, event_data->time); - } - - /* If we had scheduled updates that couldn't run because we were - * waiting on a page flip, run them now. */ - if (compositor->scheduled_updates) - perform_update(compositor); - - break; - } - } -} - static void handle_terminate(uint32_t time, uint32_t value, void * data) { struct wl_display * display = data; @@ -664,10 +760,10 @@ bool swc_compositor_initialize(struct swc_compositor * compositor, struct wl_display * display, struct wl_event_loop * event_loop) { + struct swc_screen_internal * screen; uint32_t keysym; compositor->display = display; - compositor->drm_listener.notify = &handle_drm_event; compositor->scheduled_updates = 0; compositor->pending_flips = 0; compositor->pointer_handler = (struct swc_pointer_handler) { @@ -675,12 +771,13 @@ bool swc_compositor_initialize(struct swc_compositor * compositor, .motion = &handle_motion }; - wl_signal_add(&swc.drm->event_signal, &compositor->drm_listener); - pixman_region32_init(&compositor->damage); pixman_region32_init(&compositor->opaque); wl_list_init(&compositor->views); + wl_list_for_each(screen, &swc.screens, link) + screen_new(screen); + swc_add_key_binding(SWC_MOD_CTRL | SWC_MOD_ALT, XKB_KEY_BackSpace, &handle_terminate, display); diff --git a/libswc/compositor.h b/libswc/compositor.h @@ -28,8 +28,6 @@ struct swc_compositor }; struct swc_pointer_handler pointer_handler; - - struct wl_listener drm_listener; }; bool swc_compositor_initialize(struct swc_compositor * compositor, diff --git a/libswc/cursor_plane.c b/libswc/cursor_plane.c @@ -0,0 +1,107 @@ +/* swc: cursor_plane.c + * + * Copyright (c) 2013, 2014 Michael Forney + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "cursor_plane.h" +#include "buffer.h" +#include "drm.h" +#include "internal.h" +#include "util.h" + +#include <errno.h> +#include <wld/wld.h> +#include <wld/drm.h> +#include <xf86drmMode.h> + +static bool update(struct swc_view * view) +{ + return true; +} + +static bool attach(struct swc_view * view, struct swc_buffer * buffer) +{ + struct swc_cursor_plane * plane = CONTAINER_OF(view, typeof(*plane), view); + + if (buffer) + { + union wld_object object; + + if (!wld_export(buffer->wld, WLD_DRM_OBJECT_HANDLE, &object)) + { + ERROR("Could not get export buffer to DRM handle\n"); + return false; + } + + if (drmModeSetCursor(swc.drm->fd, plane->crtc, object.u32, + buffer->wld->width, buffer->wld->height) != 0) + { + ERROR("Could not set cursor: %s\n", strerror(errno)); + return false; + } + } + else + { + if (drmModeSetCursor(swc.drm->fd, plane->crtc, 0, 0, 0) != 0) + { + ERROR("Could not unset cursor: %s\n", strerror(errno)); + return false; + } + } + + return true; +} + +static bool move(struct swc_view * view, int32_t x, int32_t y) +{ + struct swc_cursor_plane * plane = CONTAINER_OF(view, typeof(*plane), view); + + if (drmModeMoveCursor(swc.drm->fd, plane->crtc, x, y) != 0) + { + ERROR("Could not move cursor: %s\n", strerror(errno)); + return false; + } + + return true; +} + +static const struct swc_view_impl view_impl = { + .update = &update, + .attach = &attach, + .move = &move +}; + +bool swc_cursor_plane_initialize(struct swc_cursor_plane * plane, uint32_t crtc) +{ + if (drmModeSetCursor(swc.drm->fd, crtc, 0, 0, 0) != 0) + return false; + + plane->crtc = crtc; + swc_view_initialize(&plane->view, &view_impl); + + return true; +} + +void swc_cursor_plane_finalize(struct swc_cursor_plane * plane) +{ + drmModeSetCursor(swc.drm->fd, plane->crtc, 0, 0, 0); +} + diff --git a/libswc/cursor_plane.h b/libswc/cursor_plane.h @@ -0,0 +1,41 @@ +/* swc: libswc/cursor_plane.h + * + * Copyright (c) 2013, 2014 Michael Forney + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef SWC_CURSOR_PLANE_H +#define SWC_CURSOR_PLANE_H + +#include "view.h" + +struct swc_cursor_plane +{ + struct swc_view view; + uint32_t crtc; +}; + +bool swc_cursor_plane_initialize(struct swc_cursor_plane * plane, + uint32_t crtc); + +void swc_cursor_plane_finalize(struct swc_cursor_plane * plane); + +#endif + diff --git a/libswc/drm.c b/libswc/drm.c @@ -26,6 +26,7 @@ #include "internal.h" #include "output.h" #include "screen.h" +#include "util.h" #include "wayland_buffer.h" #include <stdio.h> @@ -267,15 +268,9 @@ static void handle_vblank(int fd, unsigned int sequence, unsigned int sec, static void handle_page_flip(int fd, unsigned int sequence, unsigned int sec, unsigned int usec, void * data) { - struct swc_output * output = data; - struct swc_drm_event_data event_data = { - .time = sec * 1000 + usec / 1000, - .output = output - }; - - /* XXX: It doesn't make sense for multiple things to be listening for page - * flips (or does it?). Maybe this should be a callback instead? */ - swc_send_event(&swc.drm->event_signal, SWC_DRM_PAGE_FLIP, &event_data); + struct swc_drm_handler * handler = data; + + handler->page_flip(handler, sec * 1000 + usec / 1000); } static drmEventContext event_context = { @@ -315,8 +310,6 @@ bool swc_drm_initialize(const char * seat_name) const char * sysnum; char * end; - wl_signal_init(&swc.drm->event_signal); - struct udev_device * drm_device = find_primary_drm_device(seat_name); if (!drm_device) @@ -468,7 +461,7 @@ bool swc_drm_create_screens(struct wl_list * screens) break; } - if (!(output = swc_output_new(resources->crtcs[crtc_index], connector))) + if (!(output = swc_output_new(connector))) continue; output->screen = swc_screen_new(resources->crtcs[crtc_index], diff --git a/libswc/drm.h b/libswc/drm.h @@ -5,15 +5,9 @@ #include <stdint.h> #include <wayland-server.h> -enum swc_drm_event_type +struct swc_drm_handler { - SWC_DRM_PAGE_FLIP -}; - -struct swc_drm_event_data -{ - uint32_t time; - struct swc_output * output; + void (* page_flip)(struct swc_drm_handler * handler, uint32_t time); }; struct swc_drm @@ -21,7 +15,6 @@ struct swc_drm int fd; struct wld_context * context; struct wld_renderer * renderer; - struct wl_signal event_signal; }; bool swc_drm_initialize(const char * seat); diff --git a/libswc/framebuffer_plane.c b/libswc/framebuffer_plane.c @@ -0,0 +1,208 @@ +/* swc: framebuffer_plane.c + * + * Copyright (c) 2013, 2014 Michael Forney + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "framebuffer_plane.h" +#include "buffer.h" +#include "drm.h" +#include "internal.h" +#include "util.h" + +#include <errno.h> +#include <wld/wld.h> +#include <wld/drm.h> +#include <xf86drm.h> +#include <xf86drmMode.h> + +struct framebuffer +{ + uint32_t id; + struct wl_listener destroy_listener; +}; + +static void handle_buffer_destroy(struct wl_listener * listener, void * data) +{ + struct framebuffer * framebuffer + = CONTAINER_OF(listener, typeof(*framebuffer), destroy_listener); + + drmModeRmFB(swc.drm->fd, framebuffer->id); + free(framebuffer); +} + +static struct framebuffer * framebuffer_get(struct swc_buffer * buffer) +{ + struct wl_listener * listener + = wl_signal_get(&buffer->destroy_signal, &handle_buffer_destroy); + struct framebuffer * framebuffer; + + if (listener) + { + framebuffer = CONTAINER_OF(listener, typeof(*framebuffer), + destroy_listener); + } + else + { + struct wld_buffer * wld = buffer->wld; + union wld_object object; + + if (!wld_export(wld, WLD_DRM_OBJECT_HANDLE, &object)) + { + ERROR("Could not get buffer handle\n"); + goto error0; + } + + + if (!(framebuffer = malloc(sizeof *framebuffer))) + goto error0; + + if (drmModeAddFB(swc.drm->fd, wld->width, wld->height, 24, 32, + wld->pitch, object.u32, &framebuffer->id) != 0) + { + goto error1; + } + + framebuffer->destroy_listener.notify = &handle_buffer_destroy; + wl_signal_add(&buffer->destroy_signal, &framebuffer->destroy_listener); + } + + return framebuffer; + + error1: + free(framebuffer); + error0: + return NULL; +} + +static bool update(struct swc_view * view) +{ + return true; +} + +static bool attach(struct swc_view * view, struct swc_buffer * buffer) +{ + struct swc_framebuffer_plane * plane + = CONTAINER_OF(view, typeof(*plane), view); + struct framebuffer * framebuffer = framebuffer_get(buffer); + + if (!framebuffer) + return false; + + if (drmModePageFlip(swc.drm->fd, plane->crtc, framebuffer->id, + DRM_MODE_PAGE_FLIP_EVENT, &plane->drm_handler) != 0) + { + if (errno == EINVAL) + { + WARNING("Page flip failed with EINVAL, trying to set CRTC\n"); + + if (drmModeSetCrtc(swc.drm->fd, plane->crtc, framebuffer->id, 0, 0, + plane->connectors.data, + plane->connectors.size / 4, + &plane->mode.info) == 0) + { + swc_view_frame(&plane->view, swc_time()); + } + else + { + ERROR("Could not set CRTC to next framebuffer: %s\n", + strerror(errno)); + return false; + } + } + else + { + ERROR("Could not schedule page flip: %s\n", strerror(errno)); + return false; + } + } + + return true; +} + +const static struct swc_view_impl view_impl = { + .update = &update, + .attach = &attach +}; + +static void handle_page_flip(struct swc_drm_handler * handler, uint32_t time) +{ + struct swc_framebuffer_plane * plane + = CONTAINER_OF(handler, typeof(*plane), drm_handler); + + swc_view_frame(&plane->view, time); +} + +bool swc_framebuffer_plane_initialize(struct swc_framebuffer_plane * plane, + uint32_t crtc, drmModeModeInfoPtr mode, + uint32_t * connectors, + uint32_t num_connectors) +{ + uint32_t * plane_connectors; + + if (!(plane->original_crtc_state = drmModeGetCrtc(swc.drm->fd, crtc))) + { + ERROR("Failed to get CRTC state for CRTC %u: %s\n", + crtc, strerror(errno)); + goto error0; + } + + wl_array_init(&plane->connectors); + plane_connectors = wl_array_add(&plane->connectors, + num_connectors * sizeof connectors[0]); + + if (!plane_connectors) + { + ERROR("Failed to allocate connector array\n"); + goto error1; + } + + memcpy(plane_connectors, connectors, num_connectors * sizeof connectors[0]); + + if (drmModeSetCrtc(swc.drm->fd, crtc, -1, 0, 0, + connectors, num_connectors, mode) != 0) + { + ERROR("Failed to set CRTC: %s\n", strerror(errno)); + goto error2; + } + + plane->crtc = crtc; + plane->drm_handler.page_flip = &handle_page_flip; + swc_view_initialize(&plane->view, &view_impl); + swc_mode_initialize(&plane->mode, mode); + + return true; + + error2: + wl_array_release(&plane->connectors); + error1: + drmModeFreeCrtc(plane->original_crtc_state); + error0: + return false; +} + +void swc_framebuffer_plane_finalize(struct swc_framebuffer_plane * plane) +{ + drmModeCrtcPtr crtc = plane->original_crtc_state; + drmModeSetCrtc(swc.drm->fd, crtc->crtc_id, crtc->buffer_id, + crtc->x, crtc->y, NULL, 0, &crtc->mode); + drmModeFreeCrtc(crtc); +} + diff --git a/libswc/framebuffer_plane.h b/libswc/framebuffer_plane.h @@ -0,0 +1,52 @@ +/* swc: libswc/framebuffer_plane.h + * + * Copyright (c) 2013 Michael Forney + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef SWC_FRAMEBUFFER_PLANE_H +#define SWC_FRAMEBUFFER_PLANE_H + +#include "drm.h" +#include "mode.h" +#include "surface.h" +#include "view.h" + +#include <xf86drmMode.h> + +struct swc_framebuffer_plane +{ + uint32_t crtc; + drmModeCrtcPtr original_crtc_state; + struct swc_mode mode; + struct swc_view view; + struct swc_drm_handler drm_handler; + struct wl_array connectors; +}; + +bool swc_framebuffer_plane_initialize(struct swc_framebuffer_plane * plane, + uint32_t crtc, drmModeModeInfoPtr mode, + uint32_t * connectors, + uint32_t num_connectors); + +void swc_framebuffer_plane_finalize(struct swc_framebuffer_plane * plane); + +#endif + diff --git a/libswc/local.mk b/libswc/local.mk @@ -33,9 +33,10 @@ SWC_SOURCES = \ libswc/view.c \ libswc/buffer.c \ libswc/wayland_buffer.c \ + libswc/cursor_plane.c \ + libswc/framebuffer_plane.c \ libswc/util.c \ libswc/output.c \ - libswc/plane.c \ libswc/surface.c \ libswc/region.c \ libswc/input_focus.c \ diff --git a/libswc/mode.c b/libswc/mode.c @@ -39,3 +39,10 @@ void swc_mode_finish(struct swc_mode * mode) { } +bool swc_mode_equal(const struct swc_mode * mode1, const struct swc_mode * mode2) +{ + return mode1->width == mode2->width + && mode1->height == mode2->height + && mode1->refresh == mode2->refresh; +} + diff --git a/libswc/mode.h b/libswc/mode.h @@ -44,5 +44,7 @@ struct swc_mode bool swc_mode_initialize(struct swc_mode * mode, drmModeModeInfo * mode_info); void swc_mode_finish(struct swc_mode * mode); +bool swc_mode_equal(const struct swc_mode * mode1, const struct swc_mode * mode2); + #endif diff --git a/libswc/output.c b/libswc/output.c @@ -38,7 +38,7 @@ static void bind_output(struct wl_client * client, void * data, flags = 0; if (mode->preferred) flags |= WL_OUTPUT_MODE_PREFERRED; - if (output->current_mode == mode) + if (swc_mode_equal(&screen->planes.framebuffer.mode, mode)) flags |= WL_OUTPUT_MODE_CURRENT; wl_output_send_mode(resource, flags, mode->width, mode->height, @@ -49,11 +49,9 @@ static void bind_output(struct wl_client * client, void * data, wl_output_send_done(resource); } -struct swc_output * swc_output_new(uint32_t crtc_id, drmModeConnector * connector) +struct swc_output * swc_output_new(drmModeConnectorPtr connector) { struct swc_output * output; - drmModeEncoder * encoder; - drmModeCrtc * current_crtc; struct swc_mode * modes; uint32_t index; @@ -80,46 +78,21 @@ struct swc_output * swc_output_new(uint32_t crtc_id, drmModeConnector * connecto pixman_region32_init(&output->current_damage); pixman_region32_init(&output->previous_damage); - output->crtc = crtc_id; output->connector = connector->connector_id; - /* Determine the current CRTC of this output. */ - encoder = drmModeGetEncoder(swc.drm->fd, connector->encoder_id); - current_crtc = drmModeGetCrtc(swc.drm->fd, encoder->crtc_id); - drmModeFreeEncoder(encoder); - modes = wl_array_add(&output->modes, connector->count_modes * sizeof *modes); + if (!modes) + goto error2; + for (index = 0; index < connector->count_modes; ++index) { swc_mode_initialize(&modes[index], &connector->modes[index]); - if (memcmp(&modes[index].info, &current_crtc->mode, - sizeof(drmModeModeInfo)) == 0) - output->current_mode = &modes[index]; if (modes[index].preferred) output->preferred_mode = &modes[index]; } - if (output->preferred_mode) - output->current_mode = output->preferred_mode; - - output->original_state.crtc = current_crtc; - - /* Create output planes */ - if (!swc_plane_initialize(&output->framebuffer_plane, - &swc_framebuffer_plane, output)) - { - printf("failed to initialize framebuffer plane\n"); - goto error2; - } - - if (!swc_plane_initialize(&output->cursor_plane, &swc_cursor_plane, output)) - { - printf("failed to initialize cursor plane\n"); - goto error2; - } - return output; error2: @@ -133,15 +106,11 @@ struct swc_output * swc_output_new(uint32_t crtc_id, drmModeConnector * connecto void swc_output_destroy(struct swc_output * output) { struct swc_mode * mode; - drmModeCrtc * crtc = output->original_state.crtc; wl_array_for_each(mode, &output->modes) swc_mode_finish(mode); wl_array_release(&output->modes); - drmModeSetCrtc(swc.drm->fd, crtc->crtc_id, crtc->buffer_id, crtc->x, - crtc->y, &output->connector, 1, &crtc->mode); - drmModeFreeCrtc(crtc); wl_global_destroy(output->global); free(output); } diff --git a/libswc/output.h b/libswc/output.h @@ -1,8 +1,6 @@ #ifndef SWC_OUTPUT_H #define SWC_OUTPUT_H -#include "plane.h" - #include <stdint.h> #include <pixman.h> #include <wayland-util.h> @@ -18,28 +16,19 @@ struct swc_output uint32_t physical_width, physical_height; struct wl_array modes; - struct swc_mode * current_mode, * preferred_mode; - - /* Output planes. */ - struct swc_plane framebuffer_plane; - struct swc_plane cursor_plane; + struct swc_mode * preferred_mode; pixman_region32_t current_damage, previous_damage; /* The DRM connector corresponding to this output */ - uint32_t crtc, connector; - - struct - { - drmModeCrtc * crtc; - } original_state; + uint32_t connector; struct wl_global * global; struct wl_list resources; struct wl_list link; }; -struct swc_output * swc_output_new(uint32_t crtc_id, drmModeConnector * connector); +struct swc_output * swc_output_new(drmModeConnector * connector); void swc_output_destroy(struct swc_output * output); #endif diff --git a/libswc/plane.c b/libswc/plane.c @@ -1,224 +0,0 @@ -/* swc: plane.c - * - * Copyright (c) 2013 Michael Forney - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "plane.h" -#include "drm.h" -#include "internal.h" -#include "mode.h" -#include "output.h" -#include "screen.h" - -#include <stdlib.h> -#include <stdio.h> -#include <libdrm/drm.h> -#include <wld/wld.h> -#include <wld/drm.h> - -struct framebuffer -{ - struct wld_buffer * buffer; - uint32_t id; -}; - -static bool framebuffer_initialize(struct swc_plane * plane) -{ - struct framebuffer * fb = swc_double_buffer_front(&plane->double_buffer); - - return drmModeSetCrtc(swc.drm->fd, plane->output->crtc, - fb->id, 0, 0, &plane->output->connector, 1, - &plane->output->current_mode->info) == 0; -} - -static void * framebuffer_create_buffer(struct swc_plane * plane) -{ - struct wld_buffer * buffer; - struct swc_output * output = plane->output; - struct framebuffer * fb; - union wld_object object; - - if (!(fb = malloc(sizeof *fb))) - goto error0; - - buffer = wld_create_buffer(swc.drm->context, - output->screen->base.geometry.width, - output->screen->base.geometry.height, - WLD_FORMAT_XRGB8888); - - if (!buffer) - { - fprintf(stderr, "Could not create DRM buffer for framebuffer\n"); - goto error1; - } - - wld_export(buffer, WLD_DRM_OBJECT_HANDLE, &object); - - if (drmModeAddFB(swc.drm->fd, buffer->width, buffer->height, - 24, 32, buffer->pitch, object.u32, &fb->id) != 0) - { - fprintf(stderr, "drmModeAddFB failed\n"); - goto error2; - } - - fb->buffer = buffer; - - return buffer; - - error2: - wld_destroy_buffer(buffer); - error1: - free(buffer); - error0: - return NULL; -} - -static void framebuffer_destroy_buffer(struct swc_plane * plane, void * data) -{ - struct framebuffer * fb = data; - - drmModeRmFB(swc.drm->fd, fb->id); - wld_destroy_buffer(fb->buffer); -} - -static struct wld_buffer * framebuffer_get_buffer(void * data) -{ - struct framebuffer * fb = data; - - return fb->buffer; -} - -static bool framebuffer_flip(struct swc_plane * plane) -{ - struct swc_output * output = plane->output; - struct framebuffer * fb = swc_double_buffer_back(&plane->double_buffer); - - return drmModePageFlip(swc.drm->fd, output->crtc, fb->id, - DRM_MODE_PAGE_FLIP_EVENT, output) == 0; -} - -const struct swc_plane_interface swc_framebuffer_plane = { - .initialize = &framebuffer_initialize, - .create_buffer = &framebuffer_create_buffer, - .destroy_buffer = &framebuffer_destroy_buffer, - .get_buffer = &framebuffer_get_buffer, - .flip = &framebuffer_flip -}; - -static bool cursor_initialize(struct swc_plane * plane) -{ - return true; -} - -static void * cursor_create_buffer(struct swc_plane * plane) -{ - return wld_create_buffer(swc.drm->context, 64, 64, WLD_FORMAT_ARGB8888); -} - -static void cursor_destroy_buffer(struct swc_plane * plane, void * data) -{ - struct wld_buffer * buffer = data; - - wld_destroy_buffer(buffer); -} - -static struct wld_buffer * cursor_get_buffer(void * data) -{ - return data; -} - -static bool cursor_flip(struct swc_plane * plane) -{ - struct wld_buffer * buffer = swc_double_buffer_back(&plane->double_buffer); - union wld_object object; - - wld_export(buffer, WLD_DRM_OBJECT_HANDLE, &object); - return drmModeSetCursor(swc.drm->fd, plane->output->crtc, - object.u32, 64, 64) == 0; -} - -static bool cursor_move(struct swc_plane * plane, int32_t x, int32_t y) -{ - return drmModeMoveCursor(swc.drm->fd, plane->output->crtc, x, y) == 0; -} - -const struct swc_plane_interface swc_cursor_plane = { - .initialize = &cursor_initialize, - .create_buffer = &cursor_create_buffer, - .destroy_buffer = &cursor_destroy_buffer, - .get_buffer = &cursor_get_buffer, - .flip = &cursor_flip, - .move = &cursor_move -}; - -bool swc_plane_initialize(struct swc_plane * plane, - const struct swc_plane_interface * interface, - struct swc_output * output) -{ - plane->interface = interface; - plane->output = output; - plane->double_buffer.buffers[0] = interface->create_buffer(plane); - plane->double_buffer.buffers[1] = interface->create_buffer(plane); - plane->double_buffer.front = 0; - plane->x = 0; - plane->y = 0; - - return plane->interface->initialize(plane); -} - -void swc_plane_finish(struct swc_plane * plane) -{ - plane->interface->destroy_buffer(plane, plane->double_buffer.buffers[0]); - plane->interface->destroy_buffer(plane, plane->double_buffer.buffers[1]); -} - -bool swc_plane_flip(struct swc_plane * plane) -{ - if (plane->interface->flip(plane)) - { - swc_double_buffer_swap(&plane->double_buffer); - - return true; - } - else - return false; -} - -bool swc_plane_move(struct swc_plane * plane, int32_t x, int32_t y) -{ - if (plane->interface->move(plane, x, y)) - { - plane->x = x; - plane->y = y; - - return true; - } - else - return false; -} - -struct wld_buffer * swc_plane_get_buffer(struct swc_plane * plane) -{ - void * back = swc_double_buffer_back(&plane->double_buffer); - - return plane->interface->get_buffer(back); -} - diff --git a/libswc/plane.h b/libswc/plane.h @@ -1,66 +0,0 @@ -/* swc: plane.h - * - * Copyright (c) 2013 Michael Forney - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef SWC_PLANE_H -#define SWC_PLANE_H - -#include "util.h" - -struct swc_plane; - -struct swc_plane_interface -{ - bool (* initialize)(struct swc_plane * plane); - void * (* create_buffer)(struct swc_plane * plane); - void (* destroy_buffer)(struct swc_plane * plane, void * data); - struct wld_buffer * (* get_buffer)(void * data); - bool (* flip)(struct swc_plane * plane); - bool (* move)(struct swc_plane * plane, int32_t x, int32_t y); -}; - -struct swc_plane -{ - const struct swc_plane_interface * interface; - struct swc_output * output; - - struct swc_double_buffer double_buffer; - - /* Relative to the output's origin. */ - int32_t x, y; -}; - -bool swc_plane_initialize(struct swc_plane * plane, - const struct swc_plane_interface * interface, - struct swc_output * output); - -bool swc_plane_flip(struct swc_plane * plane); - -bool swc_plane_move(struct swc_plane * plane, int32_t x, int32_t y); - -struct wld_buffer * swc_plane_get_buffer(struct swc_plane * plane); - -extern const struct swc_plane_interface swc_framebuffer_plane; -extern const struct swc_plane_interface swc_cursor_plane; - -#endif - diff --git a/libswc/screen.c b/libswc/screen.c @@ -26,6 +26,7 @@ #include "internal.h" #include "mode.h" #include "output.h" +#include "util.h" #include <stdlib.h> #include <sys/param.h> @@ -75,10 +76,28 @@ struct swc_screen_internal * swc_screen_new(uint32_t crtc, wl_list_init(&screen->outputs); wl_list_insert(&INTERNAL(screen)->outputs, &output->link); + if (!swc_framebuffer_plane_initialize(&screen->planes.framebuffer, crtc, + &output->preferred_mode->info, + &output->connector, 1)) + { + ERROR("Failed to initialize framebuffer plane\n"); + goto error1; + } + + if (!swc_cursor_plane_initialize(&screen->planes.cursor, crtc)) + { + ERROR("Failed to initialize cursor plane\n"); + goto error2; + } + swc.manager->new_screen(&screen->base); return screen; + error2: + swc_framebuffer_plane_finalize(&screen->planes.framebuffer); + error1: + free(screen); error0: return NULL; } @@ -89,6 +108,8 @@ void swc_screen_destroy(struct swc_screen_internal * screen) wl_list_for_each_safe(output, next, &screen->outputs, link) swc_output_destroy(output); + swc_framebuffer_plane_finalize(&screen->planes.framebuffer); + swc_cursor_plane_finalize(&screen->planes.cursor); free(screen); } diff --git a/libswc/screen.h b/libswc/screen.h @@ -25,6 +25,8 @@ #define SWC_SCREEN_H #include "swc.h" +#include "cursor_plane.h" +#include "framebuffer_plane.h" #include <wayland-util.h> @@ -36,6 +38,12 @@ struct swc_screen_internal uint8_t id; + struct + { + struct swc_framebuffer_plane framebuffer; + struct swc_cursor_plane cursor; + } planes; + struct wl_list outputs; struct wl_list modifiers; struct wl_list link;