Framebuffers are not guaranteed to be cleared after we allocate them. Therefore, clear them as all the other uterm-video backends do. Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
1300 lines
30 KiB
C
1300 lines
30 KiB
C
/*
|
|
* uterm - Linux User-Space Terminal
|
|
*
|
|
* Copyright (c) 2011-2012 David Herrmann <dh.herrmann@googlemail.com>
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* DRM Video backend
|
|
*/
|
|
|
|
#define EGL_EGLEXT_PROTOTYPES
|
|
#define GL_GLEXT_PROTOTYPES
|
|
|
|
#include <EGL/egl.h>
|
|
#include <EGL/eglext.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <gbm.h>
|
|
#include <GLES2/gl2.h>
|
|
#include <GLES2/gl2ext.h>
|
|
#include <inttypes.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <xf86drm.h>
|
|
#include <xf86drmMode.h>
|
|
#include "eloop.h"
|
|
#include "log.h"
|
|
#include "uterm.h"
|
|
#include "uterm_video.h"
|
|
|
|
#define LOG_SUBSYSTEM "video_drm"
|
|
|
|
static const char *mode_get_name(const struct uterm_mode *mode)
|
|
{
|
|
return mode->drm.info.name;
|
|
}
|
|
|
|
static unsigned int mode_get_width(const struct uterm_mode *mode)
|
|
{
|
|
return mode->drm.info.hdisplay;
|
|
}
|
|
|
|
static unsigned int mode_get_height(const struct uterm_mode *mode)
|
|
{
|
|
return mode->drm.info.vdisplay;
|
|
}
|
|
|
|
static int init_rb(struct uterm_display *disp, struct drm_rb *rb)
|
|
{
|
|
unsigned int stride, handle;
|
|
int ret;
|
|
struct uterm_video *video = disp->video;
|
|
|
|
rb->bo = gbm_bo_create(video->drm.gbm,
|
|
disp->current_mode->drm.info.hdisplay,
|
|
disp->current_mode->drm.info.vdisplay,
|
|
GBM_BO_FORMAT_XRGB8888,
|
|
GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
|
|
if (!rb->bo) {
|
|
log_err("cannot create gbm-bo");
|
|
return -EFAULT;
|
|
}
|
|
|
|
#ifdef BUILD_HAVE_GBM_BO_GET_PITCH
|
|
stride = gbm_bo_get_pitch(rb->bo);
|
|
#else
|
|
stride = gbm_bo_get_stride(rb->bo);
|
|
#endif
|
|
handle = gbm_bo_get_handle(rb->bo).u32;
|
|
|
|
/* TODO: how can we choose 24/32 dynamically? */
|
|
ret = drmModeAddFB(video->drm.fd,
|
|
disp->current_mode->drm.info.hdisplay,
|
|
disp->current_mode->drm.info.vdisplay,
|
|
24, 32, stride, handle, &rb->fb);
|
|
if (ret) {
|
|
log_err("cannot add drm-fb");
|
|
ret = -EFAULT;
|
|
goto err_gbm;
|
|
}
|
|
|
|
rb->image = eglCreateImageKHR(video->drm.disp, NULL,
|
|
EGL_NATIVE_PIXMAP_KHR, rb->bo, NULL);
|
|
if (!rb->image) {
|
|
log_err("cannot create egl image");
|
|
ret = -EFAULT;
|
|
goto err_fb;
|
|
}
|
|
|
|
glGenRenderbuffers(1, &rb->rb);
|
|
glBindRenderbuffer(GL_RENDERBUFFER, rb->rb);
|
|
glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, rb->image);
|
|
|
|
return 0;
|
|
|
|
err_fb:
|
|
drmModeRmFB(video->drm.fd, rb->fb);
|
|
err_gbm:
|
|
gbm_bo_destroy(rb->bo);
|
|
return ret;
|
|
}
|
|
|
|
static void destroy_rb(struct uterm_display *disp, struct drm_rb *rb)
|
|
{
|
|
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
|
glDeleteRenderbuffers(1, &rb->rb);
|
|
eglDestroyImageKHR(disp->video->drm.disp, rb->image);
|
|
drmModeRmFB(disp->video->drm.fd, rb->fb);
|
|
gbm_bo_destroy(rb->bo);
|
|
}
|
|
|
|
static int find_crtc(struct uterm_video *video, drmModeRes *res,
|
|
drmModeEncoder *enc)
|
|
{
|
|
int i, crtc;
|
|
struct uterm_display *iter;
|
|
|
|
for (i = 0; i < res->count_crtcs; ++i) {
|
|
if (enc->possible_crtcs & (1 << i)) {
|
|
crtc = res->crtcs[i];
|
|
for (iter = video->displays; iter; iter = iter->next) {
|
|
if (iter->drm.crtc_id == crtc)
|
|
break;
|
|
}
|
|
if (!iter)
|
|
return crtc;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int display_activate(struct uterm_display *disp, struct uterm_mode *mode)
|
|
{
|
|
struct uterm_video *video = disp->video;
|
|
int ret, crtc, i;
|
|
drmModeRes *res;
|
|
drmModeConnector *conn;
|
|
drmModeEncoder *enc;
|
|
|
|
if (!video || !video_is_awake(video) || !mode)
|
|
return -EINVAL;
|
|
if (display_is_online(disp))
|
|
return -EINVAL;
|
|
|
|
ret = video_do_use(disp->video);
|
|
if (ret)
|
|
return ret;
|
|
|
|
log_info("activating display %p to %ux%u", disp,
|
|
mode->drm.info.hdisplay, mode->drm.info.vdisplay);
|
|
|
|
res = drmModeGetResources(video->drm.fd);
|
|
if (!res) {
|
|
log_err("cannot get resources for display %p", disp);
|
|
return -EFAULT;
|
|
}
|
|
conn = drmModeGetConnector(video->drm.fd, disp->drm.conn_id);
|
|
if (!conn) {
|
|
log_err("cannot get connector for display %p", disp);
|
|
drmModeFreeResources(res);
|
|
return -EFAULT;
|
|
}
|
|
|
|
crtc = -1;
|
|
for (i = 0; i < conn->count_encoders; ++i) {
|
|
enc = drmModeGetEncoder(video->drm.fd, conn->encoders[i]);
|
|
if (!enc)
|
|
continue;
|
|
crtc = find_crtc(video, res, enc);
|
|
drmModeFreeEncoder(enc);
|
|
if (crtc >= 0)
|
|
break;
|
|
}
|
|
|
|
drmModeFreeConnector(conn);
|
|
drmModeFreeResources(res);
|
|
|
|
if (crtc < 0) {
|
|
log_warn("cannot find crtc for new display");
|
|
return -ENODEV;
|
|
}
|
|
|
|
disp->drm.crtc_id = crtc;
|
|
disp->drm.current_rb = 0;
|
|
disp->current_mode = mode;
|
|
disp->drm.saved_crtc = drmModeGetCrtc(video->drm.fd,
|
|
disp->drm.crtc_id);
|
|
|
|
ret = init_rb(disp, &disp->drm.rb[0]);
|
|
if (ret)
|
|
goto err_saved;
|
|
|
|
ret = init_rb(disp, &disp->drm.rb[1]);
|
|
if (ret)
|
|
goto err_rb;
|
|
|
|
glGenFramebuffers(1, &disp->drm.fb);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, disp->drm.fb);
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
GL_RENDERBUFFER, disp->drm.rb[0].rb);
|
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) !=
|
|
GL_FRAMEBUFFER_COMPLETE) {
|
|
log_err("cannot create gl-framebuffer");
|
|
ret = -EFAULT;
|
|
goto err_fb;
|
|
}
|
|
|
|
glClearColor(0, 0, 0, 1);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
GL_RENDERBUFFER, disp->drm.rb[1].rb);
|
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) !=
|
|
GL_FRAMEBUFFER_COMPLETE) {
|
|
log_warn("cannot set gl-renderbuffer");
|
|
ret = -EFAULT;
|
|
goto err_fb;
|
|
}
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
ret = drmModeSetCrtc(video->drm.fd, disp->drm.crtc_id,
|
|
disp->drm.rb[0].fb, 0, 0, &disp->drm.conn_id, 1,
|
|
&disp->current_mode->drm.info);
|
|
if (ret) {
|
|
log_err("cannot set drm-crtc");
|
|
ret = -EFAULT;
|
|
goto err_fb;
|
|
}
|
|
|
|
disp->flags |= DISPLAY_ONLINE;
|
|
return 0;
|
|
|
|
err_fb:
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
glDeleteFramebuffers(1, &disp->drm.fb);
|
|
destroy_rb(disp, &disp->drm.rb[1]);
|
|
err_rb:
|
|
destroy_rb(disp, &disp->drm.rb[0]);
|
|
err_saved:
|
|
disp->current_mode = NULL;
|
|
if (disp->drm.saved_crtc) {
|
|
drmModeFreeCrtc(disp->drm.saved_crtc);
|
|
disp->drm.saved_crtc = NULL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void display_deactivate(struct uterm_display *disp)
|
|
{
|
|
int ret;
|
|
|
|
if (!display_is_online(disp))
|
|
return;
|
|
|
|
if (disp->drm.saved_crtc) {
|
|
if (disp->video->flags & VIDEO_AWAKE) {
|
|
drmModeSetCrtc(disp->video->drm.fd,
|
|
disp->drm.saved_crtc->crtc_id,
|
|
disp->drm.saved_crtc->buffer_id,
|
|
disp->drm.saved_crtc->x,
|
|
disp->drm.saved_crtc->y,
|
|
&disp->drm.conn_id,
|
|
1,
|
|
&disp->drm.saved_crtc->mode);
|
|
}
|
|
drmModeFreeCrtc(disp->drm.saved_crtc);
|
|
disp->drm.saved_crtc = NULL;
|
|
}
|
|
|
|
ret = video_do_use(disp->video);
|
|
if (ret)
|
|
return;
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
glDeleteFramebuffers(1, &disp->drm.fb);
|
|
destroy_rb(disp, &disp->drm.rb[1]);
|
|
destroy_rb(disp, &disp->drm.rb[0]);
|
|
disp->current_mode = NULL;
|
|
disp->flags &= ~(DISPLAY_ONLINE | DISPLAY_VSYNC);
|
|
log_info("deactivating display %p", disp);
|
|
}
|
|
|
|
static int display_set_dpms(struct uterm_display *disp, int state)
|
|
{
|
|
int i, ret, set;
|
|
drmModeConnector *conn;
|
|
drmModePropertyRes *prop;
|
|
|
|
if (!display_is_conn(disp) || !video_is_awake(disp->video))
|
|
return -EINVAL;
|
|
|
|
switch (state) {
|
|
case UTERM_DPMS_ON:
|
|
set = DRM_MODE_DPMS_ON;
|
|
break;
|
|
case UTERM_DPMS_STANDBY:
|
|
set = DRM_MODE_DPMS_STANDBY;
|
|
break;
|
|
case UTERM_DPMS_SUSPEND:
|
|
set = DRM_MODE_DPMS_SUSPEND;
|
|
break;
|
|
case UTERM_DPMS_OFF:
|
|
set = DRM_MODE_DPMS_OFF;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
log_info("setting DPMS of display %p to %s", disp,
|
|
uterm_dpms_to_name(state));
|
|
|
|
conn = drmModeGetConnector(disp->video->drm.fd, disp->drm.conn_id);
|
|
if (!conn) {
|
|
log_err("cannot get display connector");
|
|
return -EFAULT;
|
|
}
|
|
|
|
ret = 0;
|
|
for (i = 0; i < conn->count_props; ++i) {
|
|
prop = drmModeGetProperty(disp->video->drm.fd, conn->props[i]);
|
|
if (!prop) {
|
|
log_error("cannot get DRM property (%d): %m", errno);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(prop->name, "DPMS")) {
|
|
ret = drmModeConnectorSetProperty(disp->video->drm.fd,
|
|
disp->drm.conn_id, prop->prop_id, set);
|
|
if (ret) {
|
|
log_info("cannot set DPMS");
|
|
ret = -EFAULT;
|
|
}
|
|
drmModeFreeProperty(prop);
|
|
break;
|
|
}
|
|
drmModeFreeProperty(prop);
|
|
}
|
|
|
|
if (i == conn->count_props) {
|
|
ret = 0;
|
|
log_warn("display does not support DPMS");
|
|
state = UTERM_DPMS_UNKNOWN;
|
|
}
|
|
|
|
drmModeFreeConnector(conn);
|
|
disp->dpms = state;
|
|
return ret;
|
|
}
|
|
|
|
static int display_use(struct uterm_display *disp)
|
|
{
|
|
int ret;
|
|
|
|
if (!display_is_online(disp))
|
|
return -EINVAL;
|
|
|
|
ret = video_do_use(disp->video);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* TODO: we need triple buffering as a VSYNC may still be pending */
|
|
glBindFramebuffer(GL_FRAMEBUFFER, disp->drm.fb);
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
GL_RENDERBUFFER, disp->drm.rb[disp->drm.current_rb ^ 1].rb);
|
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) !=
|
|
GL_FRAMEBUFFER_COMPLETE) {
|
|
log_warn("cannot set gl-renderbuffer");
|
|
return -EFAULT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int display_swap(struct uterm_display *disp)
|
|
{
|
|
int ret;
|
|
|
|
if (!display_is_online(disp) || !video_is_awake(disp->video))
|
|
return -EINVAL;
|
|
if (disp->dpms != UTERM_DPMS_ON)
|
|
return -EINVAL;
|
|
|
|
/* TODO: is glFlush sufficient here? */
|
|
glFinish();
|
|
|
|
errno = 0;
|
|
disp->drm.current_rb ^= 1;
|
|
ret = drmModePageFlip(disp->video->drm.fd, disp->drm.crtc_id,
|
|
disp->drm.rb[disp->drm.current_rb].fb,
|
|
DRM_MODE_PAGE_FLIP_EVENT, disp);
|
|
if (ret) {
|
|
log_warn("page-flip failed %d %d", ret, errno);
|
|
return -EFAULT;
|
|
}
|
|
uterm_display_ref(disp);
|
|
disp->flags |= DISPLAY_VSYNC;
|
|
|
|
return 0;
|
|
}
|
|
|
|
extern const char *gl_static_fill_vert;
|
|
extern const char *gl_static_fill_frag;
|
|
extern const char *gl_static_blend_vert;
|
|
extern const char *gl_static_blend_frag;
|
|
extern const char *gl_static_blit_vert;
|
|
extern const char *gl_static_blit_frag;
|
|
|
|
static int init_shaders(struct uterm_video *video)
|
|
{
|
|
int ret;
|
|
char *fill_attr[] = { "position", "color" };
|
|
char *blend_attr[] = { "position", "texture_position" };
|
|
char *blit_attr[] = { "position", "texture_position" };
|
|
|
|
if (video->drm.sinit == 1)
|
|
return -EFAULT;
|
|
else if (video->drm.sinit == 2)
|
|
return 0;
|
|
|
|
video->drm.sinit = 1;
|
|
|
|
ret = gl_shader_new(&video->drm.fill_shader, gl_static_fill_vert,
|
|
gl_static_fill_frag, fill_attr, 2, log_llog);
|
|
if (ret)
|
|
return ret;
|
|
|
|
video->drm.uni_fill_proj = gl_shader_get_uniform(
|
|
video->drm.fill_shader,
|
|
"projection");
|
|
|
|
ret = gl_shader_new(&video->drm.blend_shader, gl_static_blend_vert,
|
|
gl_static_blend_frag, blend_attr, 2, log_llog);
|
|
if (ret)
|
|
return ret;
|
|
|
|
video->drm.uni_blend_proj = gl_shader_get_uniform(
|
|
video->drm.blend_shader,
|
|
"projection");
|
|
video->drm.uni_blend_tex = gl_shader_get_uniform(
|
|
video->drm.blend_shader,
|
|
"texture");
|
|
video->drm.uni_blend_fgcol = gl_shader_get_uniform(
|
|
video->drm.blend_shader,
|
|
"fgcolor");
|
|
video->drm.uni_blend_bgcol = gl_shader_get_uniform(
|
|
video->drm.blend_shader,
|
|
"bgcolor");
|
|
|
|
ret = gl_shader_new(&video->drm.blit_shader, gl_static_blit_vert,
|
|
gl_static_blit_frag, blit_attr, 2, log_llog);
|
|
if (ret)
|
|
return ret;
|
|
|
|
video->drm.uni_blit_proj = gl_shader_get_uniform(
|
|
video->drm.blit_shader,
|
|
"projection");
|
|
video->drm.uni_blit_tex = gl_shader_get_uniform(
|
|
video->drm.blit_shader,
|
|
"texture");
|
|
|
|
gl_tex_new(&video->drm.tex, 1);
|
|
video->drm.sinit = 2;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void deinit_shaders(struct uterm_video *video)
|
|
{
|
|
if (video->drm.sinit == 0)
|
|
return;
|
|
|
|
video->drm.sinit = 0;
|
|
gl_tex_free(&video->drm.tex, 1);
|
|
gl_shader_unref(video->drm.blit_shader);
|
|
gl_shader_unref(video->drm.blend_shader);
|
|
gl_shader_unref(video->drm.fill_shader);
|
|
}
|
|
|
|
static int display_blit(struct uterm_display *disp,
|
|
const struct uterm_video_buffer *buf,
|
|
unsigned int x, unsigned int y)
|
|
{
|
|
unsigned int sw, sh, tmp, width, height, i;
|
|
float mat[16];
|
|
float vertices[6 * 2], texpos[6 * 2];
|
|
int ret;
|
|
uint8_t *packed, *src, *dst;
|
|
|
|
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
|
|
return -EINVAL;
|
|
if (!video_is_awake(disp->video))
|
|
return -EINVAL;
|
|
if (buf->format != UTERM_FORMAT_XRGB32)
|
|
return -EINVAL;
|
|
|
|
ret = display_use(disp);
|
|
if (ret)
|
|
return ret;
|
|
ret = init_shaders(disp->video);
|
|
if (ret)
|
|
return ret;
|
|
|
|
sw = disp->current_mode->drm.info.hdisplay;
|
|
sh = disp->current_mode->drm.info.vdisplay;
|
|
|
|
vertices[0] = -1.0;
|
|
vertices[1] = -1.0;
|
|
vertices[2] = -1.0;
|
|
vertices[3] = +1.0;
|
|
vertices[4] = +1.0;
|
|
vertices[5] = +1.0;
|
|
|
|
vertices[6] = -1.0;
|
|
vertices[7] = -1.0;
|
|
vertices[8] = +1.0;
|
|
vertices[9] = +1.0;
|
|
vertices[10] = +1.0;
|
|
vertices[11] = -1.0;
|
|
|
|
texpos[0] = 0.0;
|
|
texpos[1] = 0.0;
|
|
texpos[2] = 0.0;
|
|
texpos[3] = 1.0;
|
|
texpos[4] = 1.0;
|
|
texpos[5] = 1.0;
|
|
|
|
texpos[6] = 0.0;
|
|
texpos[7] = 0.0;
|
|
texpos[8] = 1.0;
|
|
texpos[9] = 1.0;
|
|
texpos[10] = 1.0;
|
|
texpos[11] = 0.0;
|
|
|
|
tmp = x + buf->width;
|
|
if (tmp < x || x >= sw)
|
|
return -EINVAL;
|
|
if (tmp > sw)
|
|
width = sw - x;
|
|
else
|
|
width = buf->width;
|
|
|
|
tmp = y + buf->height;
|
|
if (tmp < y || y >= sh)
|
|
return -EINVAL;
|
|
if (tmp > sh)
|
|
height = sh - y;
|
|
else
|
|
height = buf->height;
|
|
|
|
glViewport(x, y, width, height);
|
|
glDisable(GL_BLEND);
|
|
|
|
gl_shader_use(disp->video->drm.blit_shader);
|
|
|
|
gl_m4_identity(mat);
|
|
glUniformMatrix4fv(disp->video->drm.uni_blit_proj, 1, GL_FALSE, mat);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, disp->video->drm.tex);
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
|
|
if (disp->video->drm.supports_rowlen) {
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, buf->stride / 4);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, width, height, 0,
|
|
GL_BGRA_EXT, GL_UNSIGNED_BYTE, buf->data);
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
|
} else if (buf->stride == width) {
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, width, height, 0,
|
|
GL_BGRA_EXT, GL_UNSIGNED_BYTE, buf->data);
|
|
} else {
|
|
packed = malloc(width * height);
|
|
if (!packed)
|
|
return -ENOMEM;
|
|
|
|
src = buf->data;
|
|
dst = packed;
|
|
for (i = 0; i < height; ++i) {
|
|
memcpy(dst, src, width * 4);
|
|
dst += width * 4;
|
|
src += buf->stride;
|
|
}
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, width, height, 0,
|
|
GL_BGRA_EXT, GL_UNSIGNED_BYTE, packed);
|
|
|
|
free(packed);
|
|
}
|
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
|
glUniform1i(disp->video->drm.uni_blit_tex, 0);
|
|
|
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices);
|
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texpos);
|
|
glEnableVertexAttribArray(0);
|
|
glEnableVertexAttribArray(1);
|
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
glDisableVertexAttribArray(0);
|
|
glDisableVertexAttribArray(1);
|
|
|
|
if (gl_has_error(disp->video->drm.blit_shader)) {
|
|
log_warning("GL error");
|
|
return -EFAULT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int display_blend(struct uterm_display *disp,
|
|
const struct uterm_video_buffer *buf,
|
|
unsigned int x, unsigned int y,
|
|
uint8_t fr, uint8_t fg, uint8_t fb,
|
|
uint8_t br, uint8_t bg, uint8_t bb)
|
|
{
|
|
unsigned int sw, sh, tmp, width, height, i;
|
|
float mat[16];
|
|
float vertices[6 * 2], texpos[6 * 2], fgcol[3], bgcol[3];
|
|
int ret;
|
|
uint8_t *packed, *src, *dst;
|
|
|
|
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
|
|
return -EINVAL;
|
|
if (!video_is_awake(disp->video))
|
|
return -EINVAL;
|
|
if (buf->format != UTERM_FORMAT_GREY)
|
|
return -EINVAL;
|
|
|
|
ret = display_use(disp);
|
|
if (ret)
|
|
return ret;
|
|
ret = init_shaders(disp->video);
|
|
if (ret)
|
|
return ret;
|
|
|
|
sw = disp->current_mode->drm.info.hdisplay;
|
|
sh = disp->current_mode->drm.info.vdisplay;
|
|
|
|
vertices[0] = -1.0;
|
|
vertices[1] = -1.0;
|
|
vertices[2] = -1.0;
|
|
vertices[3] = +1.0;
|
|
vertices[4] = +1.0;
|
|
vertices[5] = +1.0;
|
|
|
|
vertices[6] = -1.0;
|
|
vertices[7] = -1.0;
|
|
vertices[8] = +1.0;
|
|
vertices[9] = +1.0;
|
|
vertices[10] = +1.0;
|
|
vertices[11] = -1.0;
|
|
|
|
texpos[0] = 0.0;
|
|
texpos[1] = 0.0;
|
|
texpos[2] = 0.0;
|
|
texpos[3] = 1.0;
|
|
texpos[4] = 1.0;
|
|
texpos[5] = 1.0;
|
|
|
|
texpos[6] = 0.0;
|
|
texpos[7] = 0.0;
|
|
texpos[8] = 1.0;
|
|
texpos[9] = 1.0;
|
|
texpos[10] = 1.0;
|
|
texpos[11] = 0.0;
|
|
|
|
fgcol[0] = fr / 255.0;
|
|
fgcol[1] = fg / 255.0;
|
|
fgcol[2] = fb / 255.0;
|
|
bgcol[0] = br / 255.0;
|
|
bgcol[1] = bg / 255.0;
|
|
bgcol[2] = bb / 255.0;
|
|
|
|
tmp = x + buf->width;
|
|
if (tmp < x || x >= sw)
|
|
return -EINVAL;
|
|
if (tmp > sw)
|
|
width = sw - x;
|
|
else
|
|
width = buf->width;
|
|
|
|
tmp = y + buf->height;
|
|
if (tmp < y || y >= sh)
|
|
return -EINVAL;
|
|
if (tmp > sh)
|
|
height = sh - y;
|
|
else
|
|
height = buf->height;
|
|
|
|
glViewport(x, y, width, height);
|
|
glDisable(GL_BLEND);
|
|
|
|
gl_shader_use(disp->video->drm.blend_shader);
|
|
|
|
gl_m4_identity(mat);
|
|
glUniformMatrix4fv(disp->video->drm.uni_blend_proj, 1, GL_FALSE, mat);
|
|
|
|
glUniform3fv(disp->video->drm.uni_blend_fgcol, 1, fgcol);
|
|
glUniform3fv(disp->video->drm.uni_blend_bgcol, 1, bgcol);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, disp->video->drm.tex);
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
|
|
if (disp->video->drm.supports_rowlen) {
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, buf->stride);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
|
|
GL_ALPHA, GL_UNSIGNED_BYTE, buf->data);
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
|
} else if (buf->stride == width) {
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
|
|
GL_ALPHA, GL_UNSIGNED_BYTE, buf->data);
|
|
} else {
|
|
packed = malloc(width * height);
|
|
if (!packed)
|
|
return -ENOMEM;
|
|
|
|
src = buf->data;
|
|
dst = packed;
|
|
for (i = 0; i < height; ++i) {
|
|
memcpy(dst, src, width);
|
|
dst += width;
|
|
src += buf->stride;
|
|
}
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
|
|
GL_ALPHA, GL_UNSIGNED_BYTE, packed);
|
|
|
|
free(packed);
|
|
}
|
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
|
glUniform1i(disp->video->drm.uni_blend_tex, 0);
|
|
|
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices);
|
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texpos);
|
|
glEnableVertexAttribArray(0);
|
|
glEnableVertexAttribArray(1);
|
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
glDisableVertexAttribArray(0);
|
|
glDisableVertexAttribArray(1);
|
|
|
|
if (gl_has_error(disp->video->drm.blend_shader)) {
|
|
log_warning("GL error");
|
|
return -EFAULT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int display_fake_blendv(struct uterm_display *disp,
|
|
const struct uterm_video_blend_req *req,
|
|
size_t num)
|
|
{
|
|
int ret;
|
|
unsigned int i;
|
|
|
|
if (!disp || !req)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < num; ++i, ++req) {
|
|
if (!req->buf)
|
|
continue;
|
|
|
|
ret = display_blend(disp, req->buf, req->x, req->y,
|
|
req->fr, req->fg, req->fb,
|
|
req->br, req->bg, req->bb);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int display_fill(struct uterm_display *disp,
|
|
uint8_t r, uint8_t g, uint8_t b,
|
|
unsigned int x, unsigned int y,
|
|
unsigned int width, unsigned int height)
|
|
{
|
|
unsigned int sw, sh, tmp, i;
|
|
float mat[16];
|
|
float vertices[6 * 2], colors[6 * 4];
|
|
int ret;
|
|
|
|
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
|
|
return -EINVAL;
|
|
if (!video_is_awake(disp->video))
|
|
return -EINVAL;
|
|
|
|
ret = display_use(disp);
|
|
if (ret)
|
|
return ret;
|
|
ret = init_shaders(disp->video);
|
|
if (ret)
|
|
return ret;
|
|
|
|
sw = disp->current_mode->drm.info.hdisplay;
|
|
sh = disp->current_mode->drm.info.vdisplay;
|
|
|
|
for (i = 0; i < 6; ++i) {
|
|
colors[i * 4 + 0] = r / 255.0;
|
|
colors[i * 4 + 1] = g / 255.0;
|
|
colors[i * 4 + 2] = b / 255.0;
|
|
colors[i * 4 + 3] = 1.0;
|
|
}
|
|
|
|
vertices[0] = -1.0;
|
|
vertices[1] = -1.0;
|
|
vertices[2] = -1.0;
|
|
vertices[3] = +1.0;
|
|
vertices[4] = +1.0;
|
|
vertices[5] = +1.0;
|
|
|
|
vertices[6] = -1.0;
|
|
vertices[7] = -1.0;
|
|
vertices[8] = +1.0;
|
|
vertices[9] = +1.0;
|
|
vertices[10] = +1.0;
|
|
vertices[11] = -1.0;
|
|
|
|
tmp = x + width;
|
|
if (tmp < x || x >= sw)
|
|
return -EINVAL;
|
|
if (tmp > sw)
|
|
width = sw - x;
|
|
tmp = y + height;
|
|
if (tmp < y || y >= sh)
|
|
return -EINVAL;
|
|
if (tmp > sh)
|
|
height = sh - y;
|
|
|
|
glViewport(x, y, width, height);
|
|
glDisable(GL_BLEND);
|
|
|
|
gl_shader_use(disp->video->drm.fill_shader);
|
|
gl_m4_identity(mat);
|
|
glUniformMatrix4fv(disp->video->drm.uni_fill_proj, 1, GL_FALSE, mat);
|
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices);
|
|
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, colors);
|
|
glEnableVertexAttribArray(0);
|
|
glEnableVertexAttribArray(1);
|
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
glDisableVertexAttribArray(0);
|
|
glDisableVertexAttribArray(1);
|
|
|
|
if (gl_has_error(disp->video->drm.fill_shader)) {
|
|
log_warning("GL error");
|
|
return -EFAULT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void show_displays(struct uterm_video *video)
|
|
{
|
|
int ret;
|
|
struct uterm_display *iter;
|
|
|
|
if (!video_is_awake(video))
|
|
return;
|
|
|
|
for (iter = video->displays; iter; iter = iter->next) {
|
|
if (!display_is_online(iter))
|
|
continue;
|
|
if (iter->dpms != UTERM_DPMS_ON)
|
|
continue;
|
|
|
|
ret = drmModeSetCrtc(video->drm.fd, iter->drm.crtc_id,
|
|
iter->drm.rb[iter->drm.current_rb].fb, 0, 0,
|
|
&iter->drm.conn_id, 1, &iter->current_mode->drm.info);
|
|
if (ret) {
|
|
log_err("cannot set drm-crtc on display %p", iter);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int get_dpms(struct uterm_display *disp, drmModeConnector *conn)
|
|
{
|
|
int i, ret;
|
|
drmModePropertyRes *prop;
|
|
|
|
for (i = 0; i < conn->count_props; ++i) {
|
|
prop = drmModeGetProperty(disp->video->drm.fd, conn->props[i]);
|
|
if (!prop) {
|
|
log_error("cannot get DRM property (%d): %m", errno);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(prop->name, "DPMS")) {
|
|
switch (conn->prop_values[i]) {
|
|
case DRM_MODE_DPMS_ON:
|
|
ret = UTERM_DPMS_ON;
|
|
break;
|
|
case DRM_MODE_DPMS_STANDBY:
|
|
ret = UTERM_DPMS_STANDBY;
|
|
break;
|
|
case DRM_MODE_DPMS_SUSPEND:
|
|
ret = UTERM_DPMS_SUSPEND;
|
|
break;
|
|
case DRM_MODE_DPMS_OFF:
|
|
default:
|
|
ret = UTERM_DPMS_OFF;
|
|
}
|
|
|
|
drmModeFreeProperty(prop);
|
|
return ret;
|
|
}
|
|
drmModeFreeProperty(prop);
|
|
}
|
|
|
|
if (i == conn->count_props)
|
|
log_warn("display does not support DPMS");
|
|
return UTERM_DPMS_UNKNOWN;
|
|
}
|
|
|
|
static void bind_display(struct uterm_video *video, drmModeRes *res,
|
|
drmModeConnector *conn)
|
|
{
|
|
struct uterm_display *disp;
|
|
struct uterm_mode *mode;
|
|
int ret, i;
|
|
|
|
ret = display_new(&disp, &drm_display_ops, video);
|
|
if (ret)
|
|
return;
|
|
|
|
for (i = 0; i < conn->count_modes; ++i) {
|
|
ret = mode_new(&mode, &drm_mode_ops);
|
|
if (ret)
|
|
continue;
|
|
mode->drm.info = conn->modes[i];
|
|
mode->next = disp->modes;
|
|
disp->modes = mode;
|
|
|
|
/* TODO: more sophisticated default-mode selection */
|
|
if (!disp->default_mode)
|
|
disp->default_mode = mode;
|
|
}
|
|
|
|
if (!disp->modes) {
|
|
log_warn("no valid mode for display found");
|
|
uterm_display_unref(disp);
|
|
return;
|
|
}
|
|
|
|
disp->drm.conn_id = conn->connector_id;
|
|
disp->flags |= DISPLAY_AVAILABLE;
|
|
disp->next = video->displays;
|
|
video->displays = disp;
|
|
disp->dpms = get_dpms(disp, conn);
|
|
log_info("display %p DPMS is %s", disp,
|
|
uterm_dpms_to_name(disp->dpms));
|
|
VIDEO_CB(video, disp, UTERM_NEW);
|
|
}
|
|
|
|
static void unbind_display(struct uterm_display *disp)
|
|
{
|
|
if (!display_is_conn(disp))
|
|
return;
|
|
|
|
VIDEO_CB(disp->video, disp, UTERM_GONE);
|
|
display_deactivate(disp);
|
|
disp->video = NULL;
|
|
disp->flags &= ~DISPLAY_AVAILABLE;
|
|
uterm_display_unref(disp);
|
|
}
|
|
|
|
static void page_flip_handler(int fd, unsigned int frame, unsigned int sec,
|
|
unsigned int usec, void *data)
|
|
{
|
|
struct uterm_display *disp = data;
|
|
|
|
uterm_display_unref(disp);
|
|
if (disp->flags & DISPLAY_VSYNC) {
|
|
disp->flags &= ~DISPLAY_VSYNC;
|
|
DISPLAY_CB(disp, UTERM_PAGE_FLIP);
|
|
}
|
|
}
|
|
|
|
static void event(struct ev_fd *fd, int mask, void *data)
|
|
{
|
|
struct uterm_video *video = data;
|
|
drmEventContext ev;
|
|
|
|
if (mask & (EV_HUP | EV_ERR)) {
|
|
log_err("error or hangup on DRM fd");
|
|
ev_eloop_rm_fd(video->drm.efd);
|
|
video->drm.efd = NULL;
|
|
return;
|
|
}
|
|
|
|
if (mask & EV_READABLE) {
|
|
memset(&ev, 0, sizeof(ev));
|
|
ev.version = DRM_EVENT_CONTEXT_VERSION;
|
|
ev.page_flip_handler = page_flip_handler;
|
|
drmHandleEvent(video->drm.fd, &ev);
|
|
}
|
|
}
|
|
|
|
static int video_init(struct uterm_video *video, const char *node)
|
|
{
|
|
const char *ext;
|
|
int ret;
|
|
EGLint major, minor;
|
|
EGLenum api;
|
|
static const EGLint ctx_att[] =
|
|
{ EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
|
|
struct drm_video *drm = &video->drm;
|
|
|
|
log_info("probing %s", node);
|
|
|
|
drm->fd = open(node, O_RDWR | O_CLOEXEC);
|
|
if (drm->fd < 0) {
|
|
log_err("cannot open drm device %s (%d): %m", node, errno);
|
|
return -EFAULT;
|
|
}
|
|
drmDropMaster(drm->fd);
|
|
|
|
drm->gbm = gbm_create_device(drm->fd);
|
|
if (!drm->gbm) {
|
|
log_err("cannot create gbm device for %s (permission denied)",
|
|
node);
|
|
ret = -EFAULT;
|
|
goto err_close;
|
|
}
|
|
|
|
drm->disp = eglGetDisplay((EGLNativeDisplayType) drm->gbm);
|
|
if (!drm->disp) {
|
|
log_err("cannot retrieve egl display for %s", node);
|
|
ret = -EFAULT;
|
|
goto err_gbm;
|
|
}
|
|
|
|
ret = eglInitialize(drm->disp, &major, &minor);
|
|
if (!ret) {
|
|
log_err("cannot init egl display for %s", node);
|
|
ret = -EFAULT;
|
|
goto err_gbm;
|
|
}
|
|
|
|
ext = eglQueryString(drm->disp, EGL_EXTENSIONS);
|
|
if (!ext || (!strstr(ext, "EGL_KHR_surfaceless_opengl") && !strstr(ext, "EGL_KHR_surfaceless_context"))) {
|
|
log_err("surfaceless opengl not supported");
|
|
ret = -EFAULT;
|
|
goto err_disp;
|
|
}
|
|
|
|
api = EGL_OPENGL_ES_API;
|
|
/* TODO: allow api = EGL_OPENGL_API */
|
|
if (!eglBindAPI(api)) {
|
|
log_err("cannot bind opengl-es api");
|
|
ret = -EFAULT;
|
|
goto err_disp;
|
|
}
|
|
|
|
drm->ctx = eglCreateContext(drm->disp, NULL, EGL_NO_CONTEXT, ctx_att);
|
|
if (!drm->ctx) {
|
|
log_err("cannot create egl context");
|
|
ret = -EFAULT;
|
|
goto err_disp;
|
|
}
|
|
|
|
if (!eglMakeCurrent(drm->disp, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
|
drm->ctx)) {
|
|
log_err("cannot activate egl context");
|
|
ret = -EFAULT;
|
|
goto err_ctx;
|
|
}
|
|
|
|
ext = (const char*)glGetString(GL_EXTENSIONS);
|
|
if (ext && strstr((const char*)ext, "GL_EXT_unpack_subimage"))
|
|
drm->supports_rowlen = true;
|
|
else
|
|
log_warning("your GL implementation does not support GL_EXT_unpack_subimage, rendering may be slower than usual");
|
|
|
|
ret = ev_eloop_new_fd(video->eloop, &drm->efd, drm->fd,
|
|
EV_READABLE, event, video);
|
|
if (ret)
|
|
goto err_noctx;
|
|
|
|
video->flags |= VIDEO_HOTPLUG;
|
|
log_info("new drm device via %s", node);
|
|
|
|
return 0;
|
|
|
|
err_noctx:
|
|
eglMakeCurrent(drm->disp,
|
|
EGL_NO_SURFACE,
|
|
EGL_NO_SURFACE,
|
|
EGL_NO_CONTEXT);
|
|
err_ctx:
|
|
eglDestroyContext(drm->disp, drm->ctx);
|
|
err_disp:
|
|
eglTerminate(drm->disp);
|
|
err_gbm:
|
|
gbm_device_destroy(drm->gbm);
|
|
err_close:
|
|
close(drm->fd);
|
|
return ret;
|
|
}
|
|
|
|
static void video_destroy(struct uterm_video *video)
|
|
{
|
|
struct drm_video *drm = &video->drm;
|
|
struct uterm_display *disp;
|
|
|
|
while ((disp = video->displays)) {
|
|
video->displays = disp->next;
|
|
disp->next = NULL;
|
|
unbind_display(disp);
|
|
}
|
|
|
|
log_info("free drm device");
|
|
ev_eloop_rm_fd(drm->efd);
|
|
|
|
video_do_use(video);
|
|
deinit_shaders(video);
|
|
|
|
eglMakeCurrent(drm->disp,
|
|
EGL_NO_SURFACE,
|
|
EGL_NO_SURFACE,
|
|
EGL_NO_CONTEXT);
|
|
eglDestroyContext(drm->disp, drm->ctx);
|
|
eglTerminate(drm->disp);
|
|
gbm_device_destroy(drm->gbm);
|
|
drmDropMaster(drm->fd);
|
|
close(drm->fd);
|
|
}
|
|
|
|
static int video_use(struct uterm_video *video)
|
|
{
|
|
if (eglGetCurrentContext() == video->drm.ctx)
|
|
return 0;
|
|
|
|
if (!eglMakeCurrent(video->drm.disp, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
|
video->drm.ctx)) {
|
|
log_err("cannot activate egl context");
|
|
return -EFAULT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hotplug(struct uterm_video *video)
|
|
{
|
|
drmModeRes *res;
|
|
drmModeConnector *conn;
|
|
struct uterm_display *disp, *tmp;
|
|
int i;
|
|
|
|
if (!video_is_awake(video) || !video_need_hotplug(video))
|
|
return 0;
|
|
|
|
res = drmModeGetResources(video->drm.fd);
|
|
if (!res) {
|
|
log_err("cannot retrieve drm resources");
|
|
return -EACCES;
|
|
}
|
|
|
|
for (disp = video->displays; disp; disp = disp->next)
|
|
disp->flags &= ~DISPLAY_AVAILABLE;
|
|
|
|
for (i = 0; i < res->count_connectors; ++i) {
|
|
conn = drmModeGetConnector(video->drm.fd, res->connectors[i]);
|
|
if (!conn)
|
|
continue;
|
|
if (conn->connection == DRM_MODE_CONNECTED) {
|
|
for (disp = video->displays; disp; disp = disp->next) {
|
|
if (disp->drm.conn_id == res->connectors[i]) {
|
|
disp->flags |= DISPLAY_AVAILABLE;
|
|
break;
|
|
}
|
|
}
|
|
if (!disp)
|
|
bind_display(video, res, conn);
|
|
}
|
|
drmModeFreeConnector(conn);
|
|
}
|
|
|
|
drmModeFreeResources(res);
|
|
|
|
while (video->displays) {
|
|
tmp = video->displays;
|
|
if (tmp->flags & DISPLAY_AVAILABLE)
|
|
break;
|
|
|
|
video->displays = tmp->next;
|
|
tmp->next = NULL;
|
|
unbind_display(tmp);
|
|
}
|
|
for (disp = video->displays; disp && disp->next; ) {
|
|
tmp = disp->next;
|
|
if (tmp->flags & DISPLAY_AVAILABLE) {
|
|
disp = tmp;
|
|
} else {
|
|
disp->next = tmp->next;
|
|
tmp->next = NULL;
|
|
unbind_display(tmp);
|
|
}
|
|
}
|
|
|
|
video->flags &= ~VIDEO_HOTPLUG;
|
|
return 0;
|
|
}
|
|
|
|
static int video_poll(struct uterm_video *video)
|
|
{
|
|
video->flags |= VIDEO_HOTPLUG;
|
|
return hotplug(video);
|
|
}
|
|
|
|
static void video_sleep(struct uterm_video *video)
|
|
{
|
|
if (!video_is_awake(video))
|
|
return;
|
|
|
|
drmDropMaster(video->drm.fd);
|
|
video->flags &= ~VIDEO_AWAKE;
|
|
}
|
|
|
|
static int video_wake_up(struct uterm_video *video)
|
|
{
|
|
int ret;
|
|
|
|
if (video_is_awake(video))
|
|
return 0;
|
|
|
|
ret = drmSetMaster(video->drm.fd);
|
|
if (ret) {
|
|
log_err("cannot set DRM-master");
|
|
return -EACCES;
|
|
}
|
|
|
|
video->flags |= VIDEO_AWAKE;
|
|
ret = hotplug(video);
|
|
if (ret) {
|
|
video->flags &= ~VIDEO_AWAKE;
|
|
drmDropMaster(video->drm.fd);
|
|
return ret;
|
|
}
|
|
|
|
show_displays(video);
|
|
return 0;
|
|
}
|
|
|
|
const struct mode_ops drm_mode_ops = {
|
|
.init = NULL,
|
|
.destroy = NULL,
|
|
.get_name = mode_get_name,
|
|
.get_width = mode_get_width,
|
|
.get_height = mode_get_height,
|
|
};
|
|
|
|
const struct display_ops drm_display_ops = {
|
|
.init = NULL,
|
|
.destroy = NULL,
|
|
.activate = display_activate,
|
|
.deactivate = display_deactivate,
|
|
.set_dpms = display_set_dpms,
|
|
.use = display_use,
|
|
.swap = display_swap,
|
|
.blit = display_blit,
|
|
.blend = display_blend,
|
|
.blendv = display_fake_blendv,
|
|
.fake_blendv = display_fake_blendv,
|
|
.fill = display_fill,
|
|
};
|
|
|
|
const struct video_ops drm_video_ops = {
|
|
.init = video_init,
|
|
.destroy = video_destroy,
|
|
.segfault = NULL, /* TODO: reset all saved CRTCs on segfault */
|
|
.use = video_use,
|
|
.poll = video_poll,
|
|
.sleep = video_sleep,
|
|
.wake_up = video_wake_up,
|
|
};
|