diff --git a/hw/xwayland/Makefile.am b/hw/xwayland/Makefile.am index f44a7ded3..3378a6656 100644 --- a/hw/xwayland/Makefile.am +++ b/hw/xwayland/Makefile.am @@ -11,6 +11,7 @@ Xwayland_CFLAGS = \ Xwayland_SOURCES = \ xwayland.c \ + xwayland-present.c \ xwayland-input.c \ xwayland-cursor.c \ xwayland-shm.c \ diff --git a/hw/xwayland/meson.build b/hw/xwayland/meson.build index 7e24c5d63..17f52b4ba 100644 --- a/hw/xwayland/meson.build +++ b/hw/xwayland/meson.build @@ -1,5 +1,6 @@ srcs = [ 'xwayland.c', + 'xwayland-present.c', 'xwayland-input.c', 'xwayland-cursor.c', 'xwayland-shm.c', diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c new file mode 100644 index 000000000..f41e864b3 --- /dev/null +++ b/hw/xwayland/xwayland-present.c @@ -0,0 +1,366 @@ +/* + * Copyright © 2018 Roman Gilg + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of the + * copyright holders not be used in advertising or publicity + * pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "xwayland.h" + +#include + +void +xwl_present_cleanup(WindowPtr window) +{ + struct xwl_window *xwl_window = xwl_window_of_top(window); + struct xwl_present_event *event, *tmp; + + if (!xwl_window) + return; + + if (xwl_window->present_window == window) { + if (xwl_window->present_frame_callback) { + wl_callback_destroy(xwl_window->present_frame_callback); + xwl_window->present_frame_callback = NULL; + } + xwl_window->present_window = NULL; + } + + /* Clear remaining buffer releases and inform Present about free ressources */ + xorg_list_for_each_entry_safe(event, tmp, &xwl_window->present_release_queue, list) { + if (event->present_window == window) { + xorg_list_del(&event->list); + event->abort = TRUE; + } + } +} + +static void +xwl_present_free_event(struct xwl_present_event *event) +{ + xorg_list_del(&event->list); + free(event); +} + +static void +xwl_present_buffer_release(void *data, struct wl_buffer *buffer) +{ + struct xwl_present_event *event = data; + + if (!event) + return; + wl_buffer_set_user_data(buffer, NULL); + + event->buffer_released = TRUE; + + if (event->abort) { + if (!event->pending) + xwl_present_free_event(event); + return; + } + + if (!event->pending) { + present_wnmd_event_notify(event->present_window, + event->event_id, + event->xwl_window->present_ust, + event->xwl_window->present_msc); + xwl_present_free_event(event); + } +} + +static const struct wl_buffer_listener xwl_present_release_listener = { + xwl_present_buffer_release +}; + +static void +xwl_present_frame_callback(void *data, + struct wl_callback *callback, + uint32_t time) +{ + struct xwl_window *xwl_window = data; + + wl_callback_destroy(xwl_window->present_frame_callback); + xwl_window->present_frame_callback = NULL; + + xwl_window->present_msc++; + xwl_window->present_ust = GetTimeInMicros(); +} + +static const struct wl_callback_listener xwl_present_frame_listener = { + xwl_present_frame_callback +}; + +static void +xwl_present_sync_callback(void *data, + struct wl_callback *callback, + uint32_t time) +{ + struct xwl_present_event *event = data; + struct xwl_window *xwl_window = event->xwl_window; + + event->pending = FALSE; + + if (event->abort) { + /* Event might have been aborted */ + if (event->buffer_released) + /* Buffer was already released, cleanup now */ + xwl_present_free_event(event); + return; + } + + present_wnmd_event_notify(event->present_window, + event->event_id, + xwl_window->present_ust, + xwl_window->present_msc); + + if (event->buffer_released) + /* If the buffer was already released, send the event now again */ + present_wnmd_event_notify(event->present_window, + event->event_id, + xwl_window->present_ust, + xwl_window->present_msc); +} + +static const struct wl_callback_listener xwl_present_sync_listener = { + xwl_present_sync_callback +}; + +static RRCrtcPtr +xwl_present_get_crtc(WindowPtr present_window) +{ + struct xwl_window *xwl_window = xwl_window_of_top(present_window); + if (xwl_window == NULL) + return NULL; + + return xwl_window->present_crtc_fake; +} + +static int +xwl_present_get_ust_msc(WindowPtr present_window, uint64_t *ust, uint64_t *msc) +{ + struct xwl_window *xwl_window = xwl_window_of_top(present_window); + if (!xwl_window) + return BadAlloc; + *ust = xwl_window->present_ust; + *msc = xwl_window->present_msc; + + return Success; +} + +static void +xwl_present_set_present_window(struct xwl_window *xwl_window, + WindowPtr present_window) +{ + if (xwl_window->present_window) + return; + + xwl_window->present_window = present_window; +} + +/* + * Queue an event to report back to the Present extension when the specified + * MSC has past + */ +static int +xwl_present_queue_vblank(WindowPtr present_window, + RRCrtcPtr crtc, + uint64_t event_id, + uint64_t msc) +{ + /* Not yet implemented + */ + return BadRequest; +} + +/* + * Remove a pending vblank event so that it is not reported + * to the extension + */ +static void +xwl_present_abort_vblank(WindowPtr present_window, + RRCrtcPtr crtc, + uint64_t event_id, + uint64_t msc) +{ + struct xwl_window *xwl_window = xwl_window_of_top(present_window); + struct xwl_present_event *event; + + if (!xwl_window) + return; + + xorg_list_for_each_entry(event, &xwl_window->present_release_queue, list) { + if (event->event_id == event_id) { + event->abort = TRUE; + return; + } + } +} + +static void +xwl_present_flush(WindowPtr window) +{ + /* Only called when a Pixmap is copied instead of flipped, + * but in this case we wait on the next block_handler. + */ +} + +static Bool +xwl_present_check_flip2(RRCrtcPtr crtc, + WindowPtr present_window, + PixmapPtr pixmap, + Bool sync_flip, + PresentFlipReason *reason) +{ + struct xwl_window *xwl_window = xwl_window_of_top(present_window); + + if (!xwl_window) + return FALSE; + + /* + * Do not flip if there is already another child window doing flips. + */ + if (xwl_window->present_window && + xwl_window->present_window != present_window) + return FALSE; + + if (!xwl_window->present_crtc_fake) + return FALSE; + /* + * Make sure the client doesn't try to flip to another crtc + * than the one created for 'xwl_window'. + */ + if (xwl_window->present_crtc_fake != crtc) + return FALSE; + + /* + * We currently only allow flips of windows, that have the same + * dimensions as their xwl_window parent window. For the case of + * different sizes subsurfaces are presumably the way forward. + */ + if (!RegionEqual(&xwl_window->window->winSize, &present_window->winSize)) + return FALSE; + + return TRUE; +} + +static Bool +xwl_present_flip(WindowPtr present_window, + RRCrtcPtr crtc, + uint64_t event_id, + uint64_t target_msc, + PixmapPtr pixmap, + Bool sync_flip, + RegionPtr damage) +{ + struct xwl_window *xwl_window = xwl_window_of_top(present_window); + BoxPtr present_box, damage_box; + Bool buffer_created; + struct wl_buffer *buffer; + struct xwl_present_event *event; + + present_box = RegionExtents(&present_window->winSize); + damage_box = RegionExtents(damage); + + xwl_present_set_present_window(xwl_window, present_window); + + event = malloc(sizeof *event); + if (!event) + return FALSE; + + buffer = xwl_glamor_pixmap_get_wl_buffer(pixmap, + present_box->x2 - present_box->x1, + present_box->y2 - present_box->y1, + &buffer_created); + + event->event_id = event_id; + event->present_window = present_window; + event->xwl_window = xwl_window; + event->buffer = buffer; + event->target_msc = xwl_window->present_msc; + event->pending = TRUE; + event->abort = FALSE; + event->buffer_released = FALSE; + + xorg_list_add(&event->list, &xwl_window->present_release_queue); + + if (buffer_created) + wl_buffer_add_listener(buffer, &xwl_present_release_listener, NULL); + wl_buffer_set_user_data(buffer, event); + + /* We can flip directly to the main surface (full screen window without clips) */ + wl_surface_attach(xwl_window->surface, buffer, 0, 0); + + if (!xwl_window->present_frame_callback) { + xwl_window->present_frame_callback = wl_surface_frame(xwl_window->surface); + wl_callback_add_listener(xwl_window->present_frame_callback, + &xwl_present_frame_listener, + xwl_window); + } + + wl_surface_damage(xwl_window->surface, 0, 0, + damage_box->x2 - damage_box->x1, + damage_box->y2 - damage_box->y1); + + wl_surface_commit(xwl_window->surface); + + xwl_window->present_sync_callback = wl_display_sync(xwl_window->xwl_screen->display); + wl_callback_add_listener(xwl_window->present_sync_callback, + &xwl_present_sync_listener, + event); + + wl_display_flush(xwl_window->xwl_screen->display); + return TRUE; +} + +static void +xwl_present_flips_stop(WindowPtr window) +{ + struct xwl_window *xwl_window = xwl_window_of_top(window); + + if (!xwl_window) + return; + + assert(xwl_window->present_window == window); + + xwl_window->present_window = NULL; +} + +static present_wnmd_info_rec xwl_present_info = { + .version = PRESENT_SCREEN_INFO_VERSION, + .get_crtc = xwl_present_get_crtc, + + .get_ust_msc = xwl_present_get_ust_msc, + .queue_vblank = xwl_present_queue_vblank, + .abort_vblank = xwl_present_abort_vblank, + + .flush = xwl_present_flush, + + .capabilities = PresentCapabilityAsync, + .check_flip2 = xwl_present_check_flip2, + .flip = xwl_present_flip, + .flips_stop = xwl_present_flips_stop +}; + +Bool +xwl_present_init(ScreenPtr screen) +{ + return present_wnmd_screen_init(screen, &xwl_present_info); +} diff --git a/hw/xwayland/xwayland.h b/hw/xwayland/xwayland.h index 49fb5ba28..dd7d1c685 100644 --- a/hw/xwayland/xwayland.h +++ b/hw/xwayland/xwayland.h @@ -68,6 +68,7 @@ struct xwl_screen { int listen_fd_count; int rootless; int glamor; + int present; CreateScreenResourcesProcPtr CreateScreenResources; CloseScreenProcPtr CloseScreen; @@ -126,6 +127,32 @@ struct xwl_window { struct xorg_list link_damage; struct wl_callback *frame_callback; Bool allow_commits; + + /* present */ + RRCrtcPtr present_crtc_fake; + struct xorg_list present_link; + WindowPtr present_window; + uint64_t present_msc; + uint64_t present_ust; + + struct wl_callback *present_frame_callback; + struct wl_callback *present_sync_callback; + struct xorg_list present_release_queue; +}; + +struct xwl_present_event { + uint64_t event_id; + uint64_t target_msc; + + Bool abort; + Bool pending; + Bool buffer_released; + + WindowPtr present_window; + struct xwl_window *xwl_window; + struct wl_buffer *buffer; + + struct xorg_list list; }; #define MODIFIER_META 0x01 @@ -346,6 +373,9 @@ struct wl_buffer *xwl_glamor_pixmap_get_wl_buffer(PixmapPtr pixmap, unsigned short height, Bool *created); +Bool xwl_present_init(ScreenPtr screen); +void xwl_present_cleanup(WindowPtr window); + void xwl_screen_release_tablet_manager(struct xwl_screen *xwl_screen); void xwl_output_get_xdg_output(struct xwl_output *xwl_output);