| #include "qemu/osdep.h" | 
 | #include "qemu/error-report.h" | 
 | #include "qemu/module.h" | 
 | #include "qapi/error.h" | 
 | #include "ui/console.h" | 
 | #include "ui/egl-helpers.h" | 
 | #include "ui/egl-context.h" | 
 | #include "ui/shader.h" | 
 |  | 
 | typedef struct egl_dpy { | 
 |     DisplayChangeListener dcl; | 
 |     DisplaySurface *ds; | 
 |     QemuGLShader *gls; | 
 |     egl_fb guest_fb; | 
 |     egl_fb cursor_fb; | 
 |     egl_fb blit_fb; | 
 |     bool y_0_top; | 
 |     uint32_t pos_x; | 
 |     uint32_t pos_y; | 
 | } egl_dpy; | 
 |  | 
 | /* ------------------------------------------------------------------ */ | 
 |  | 
 | static void egl_refresh(DisplayChangeListener *dcl) | 
 | { | 
 |     graphic_hw_update(dcl->con); | 
 | } | 
 |  | 
 | static void egl_gfx_update(DisplayChangeListener *dcl, | 
 |                            int x, int y, int w, int h) | 
 | { | 
 | } | 
 |  | 
 | static void egl_gfx_switch(DisplayChangeListener *dcl, | 
 |                            struct DisplaySurface *new_surface) | 
 | { | 
 |     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); | 
 |  | 
 |     edpy->ds = new_surface; | 
 | } | 
 |  | 
 | static QEMUGLContext egl_create_context(DisplayGLCtx *dgc, | 
 |                                         QEMUGLParams *params) | 
 | { | 
 |     eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, | 
 |                    qemu_egl_rn_ctx); | 
 |     return qemu_egl_create_context(dgc, params); | 
 | } | 
 |  | 
 | static void egl_scanout_disable(DisplayChangeListener *dcl) | 
 | { | 
 |     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); | 
 |  | 
 |     egl_fb_destroy(&edpy->guest_fb); | 
 |     egl_fb_destroy(&edpy->blit_fb); | 
 | } | 
 |  | 
 | static void egl_scanout_texture(DisplayChangeListener *dcl, | 
 |                                 uint32_t backing_id, | 
 |                                 bool backing_y_0_top, | 
 |                                 uint32_t backing_width, | 
 |                                 uint32_t backing_height, | 
 |                                 uint32_t x, uint32_t y, | 
 |                                 uint32_t w, uint32_t h, | 
 |                                 void *d3d_tex2d) | 
 | { | 
 |     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); | 
 |  | 
 |     edpy->y_0_top = backing_y_0_top; | 
 |  | 
 |     /* source framebuffer */ | 
 |     egl_fb_setup_for_tex(&edpy->guest_fb, | 
 |                          backing_width, backing_height, backing_id, false); | 
 |  | 
 |     /* dest framebuffer */ | 
 |     if (edpy->blit_fb.width  != backing_width || | 
 |         edpy->blit_fb.height != backing_height) { | 
 |         egl_fb_destroy(&edpy->blit_fb); | 
 |         egl_fb_setup_new_tex(&edpy->blit_fb, backing_width, backing_height); | 
 |     } | 
 | } | 
 |  | 
 | #ifdef CONFIG_GBM | 
 |  | 
 | static void egl_scanout_dmabuf(DisplayChangeListener *dcl, | 
 |                                QemuDmaBuf *dmabuf) | 
 | { | 
 |     uint32_t width, height, texture; | 
 |  | 
 |     egl_dmabuf_import_texture(dmabuf); | 
 |     texture = qemu_dmabuf_get_texture(dmabuf); | 
 |     if (!texture) { | 
 |         return; | 
 |     } | 
 |  | 
 |     width = qemu_dmabuf_get_width(dmabuf); | 
 |     height = qemu_dmabuf_get_height(dmabuf); | 
 |  | 
 |     egl_scanout_texture(dcl, texture, false, width, height, 0, 0, | 
 |                         width, height, NULL); | 
 | } | 
 |  | 
 | static void egl_cursor_dmabuf(DisplayChangeListener *dcl, | 
 |                               QemuDmaBuf *dmabuf, bool have_hot, | 
 |                               uint32_t hot_x, uint32_t hot_y) | 
 | { | 
 |     uint32_t width, height, texture; | 
 |     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); | 
 |  | 
 |     if (dmabuf) { | 
 |         egl_dmabuf_import_texture(dmabuf); | 
 |         texture = qemu_dmabuf_get_texture(dmabuf); | 
 |         if (!texture) { | 
 |             return; | 
 |         } | 
 |  | 
 |         width = qemu_dmabuf_get_width(dmabuf); | 
 |         height = qemu_dmabuf_get_height(dmabuf); | 
 |         egl_fb_setup_for_tex(&edpy->cursor_fb, width, height, texture, false); | 
 |     } else { | 
 |         egl_fb_destroy(&edpy->cursor_fb); | 
 |     } | 
 | } | 
 |  | 
 | static void egl_release_dmabuf(DisplayChangeListener *dcl, | 
 |                                QemuDmaBuf *dmabuf) | 
 | { | 
 |     egl_dmabuf_release_texture(dmabuf); | 
 | } | 
 |  | 
 | #endif | 
 |  | 
 | static void egl_cursor_position(DisplayChangeListener *dcl, | 
 |                                 uint32_t pos_x, uint32_t pos_y) | 
 | { | 
 |     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); | 
 |  | 
 |     edpy->pos_x = pos_x; | 
 |     edpy->pos_y = pos_y; | 
 | } | 
 |  | 
 | static void egl_scanout_flush(DisplayChangeListener *dcl, | 
 |                               uint32_t x, uint32_t y, | 
 |                               uint32_t w, uint32_t h) | 
 | { | 
 |     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); | 
 |  | 
 |     if (!edpy->guest_fb.texture || !edpy->ds) { | 
 |         return; | 
 |     } | 
 |     assert(surface_format(edpy->ds) == PIXMAN_x8r8g8b8); | 
 |  | 
 |     if (edpy->cursor_fb.texture) { | 
 |         /* have cursor -> render using textures */ | 
 |         egl_texture_blit(edpy->gls, &edpy->blit_fb, &edpy->guest_fb, | 
 |                          !edpy->y_0_top); | 
 |         egl_texture_blend(edpy->gls, &edpy->blit_fb, &edpy->cursor_fb, | 
 |                           !edpy->y_0_top, edpy->pos_x, edpy->pos_y, | 
 |                           1.0, 1.0); | 
 |     } else { | 
 |         /* no cursor -> use simple framebuffer blit */ | 
 |         egl_fb_blit(&edpy->blit_fb, &edpy->guest_fb, edpy->y_0_top); | 
 |     } | 
 |  | 
 |     egl_fb_read(edpy->ds, &edpy->blit_fb); | 
 |     dpy_gfx_update(edpy->dcl.con, x, y, w, h); | 
 | } | 
 |  | 
 | static const DisplayChangeListenerOps egl_ops = { | 
 |     .dpy_name                = "egl-headless", | 
 |     .dpy_refresh             = egl_refresh, | 
 |     .dpy_gfx_update          = egl_gfx_update, | 
 |     .dpy_gfx_switch          = egl_gfx_switch, | 
 |  | 
 |     .dpy_gl_scanout_disable  = egl_scanout_disable, | 
 |     .dpy_gl_scanout_texture  = egl_scanout_texture, | 
 | #ifdef CONFIG_GBM | 
 |     .dpy_gl_scanout_dmabuf   = egl_scanout_dmabuf, | 
 |     .dpy_gl_cursor_dmabuf    = egl_cursor_dmabuf, | 
 |     .dpy_gl_release_dmabuf   = egl_release_dmabuf, | 
 | #endif | 
 |     .dpy_gl_cursor_position  = egl_cursor_position, | 
 |     .dpy_gl_update           = egl_scanout_flush, | 
 | }; | 
 |  | 
 | static bool | 
 | egl_is_compatible_dcl(DisplayGLCtx *dgc, | 
 |                       DisplayChangeListener *dcl) | 
 | { | 
 |     if (!dcl->ops->dpy_gl_update) { | 
 |         /* | 
 |          * egl-headless is compatible with all 2d listeners, as it blits the GL | 
 |          * updates on the 2d console surface. | 
 |          */ | 
 |         return true; | 
 |     } | 
 |  | 
 |     return dcl->ops == &egl_ops; | 
 | } | 
 |  | 
 | static const DisplayGLCtxOps eglctx_ops = { | 
 |     .dpy_gl_ctx_is_compatible_dcl = egl_is_compatible_dcl, | 
 |     .dpy_gl_ctx_create       = egl_create_context, | 
 |     .dpy_gl_ctx_destroy      = qemu_egl_destroy_context, | 
 |     .dpy_gl_ctx_make_current = qemu_egl_make_context_current, | 
 | }; | 
 |  | 
 | static void early_egl_headless_init(DisplayOptions *opts) | 
 | { | 
 |     DisplayGLMode mode = DISPLAYGL_MODE_ON; | 
 |  | 
 |     if (opts->has_gl) { | 
 |         mode = opts->gl; | 
 |     } | 
 |  | 
 |     egl_init(opts->u.egl_headless.rendernode, mode, &error_fatal); | 
 | } | 
 |  | 
 | static void egl_headless_init(DisplayState *ds, DisplayOptions *opts) | 
 | { | 
 |     QemuConsole *con; | 
 |     egl_dpy *edpy; | 
 |     int idx; | 
 |  | 
 |     for (idx = 0;; idx++) { | 
 |         DisplayGLCtx *ctx; | 
 |  | 
 |         con = qemu_console_lookup_by_index(idx); | 
 |         if (!con || !qemu_console_is_graphic(con)) { | 
 |             break; | 
 |         } | 
 |  | 
 |         edpy = g_new0(egl_dpy, 1); | 
 |         edpy->dcl.con = con; | 
 |         edpy->dcl.ops = &egl_ops; | 
 |         edpy->gls = qemu_gl_init_shader(); | 
 |         ctx = g_new0(DisplayGLCtx, 1); | 
 |         ctx->ops = &eglctx_ops; | 
 |         qemu_console_set_display_gl_ctx(con, ctx); | 
 |         register_displaychangelistener(&edpy->dcl); | 
 |     } | 
 | } | 
 |  | 
 | static QemuDisplay qemu_display_egl = { | 
 |     .type       = DISPLAY_TYPE_EGL_HEADLESS, | 
 |     .early_init = early_egl_headless_init, | 
 |     .init       = egl_headless_init, | 
 | }; | 
 |  | 
 | static void register_egl(void) | 
 | { | 
 |     qemu_display_register(&qemu_display_egl); | 
 | } | 
 |  | 
 | type_init(register_egl); | 
 |  | 
 | module_dep("ui-opengl"); |