[weston,2/2,v5] clients/simple-egl-lease: A demo client for DRM leases

Submitted by Marius-cristian Vlad on May 29, 2018, 10:04 a.m.

Details

Message ID 20180529100406.17503-3-marius-cristian.vlad@nxp.com
State New
Series "DRM lease support"
Headers show

Commit Message

Marius-cristian Vlad May 29, 2018, 10:04 a.m.
Heavily inspired by kmscube and simple-egl. Uses legacy page-flipping and
triangle shaders from simple-egl.

Signed-off-by: Marius Vlad <marius-cristian.vlad@nxp.com>
---
 Makefile.am                |  11 +
 clients/simple-egl-lease.c | 887 +++++++++++++++++++++++++++++++++++++++++++++
 clients/simple-egl-lease.h |  99 +++++
 configure.ac               |  17 +
 4 files changed, 1014 insertions(+)
 create mode 100644 clients/simple-egl-lease.c
 create mode 100644 clients/simple-egl-lease.h

Patch hide | download patch | download mbox

diff --git a/Makefile.am b/Makefile.am
index 00a49d9..abde6f2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -649,6 +649,17 @@  weston_simple_dmabuf_v4l_CFLAGS = $(AM_CFLAGS) $(SIMPLE_DMABUF_V4L_CLIENT_CFLAGS
 weston_simple_dmabuf_v4l_LDADD = $(SIMPLE_DMABUF_V4L_CLIENT_LIBS) libshared.la
 endif
 
+if BUILD_SIMPLE_EGL_LEASE_CLIENTS
+demo_clients += weston-simple-egl-lease
+weston_simple_egl_lease_SOURCES = clients/simple-egl-lease.c
+nodist_weston_simple_egl_lease_SOURCES = protocol/drm-lease-unstable-v1-protocol.c \
+	protocol/drm-lease-unstable-v1-client-protocol.h
+weston_simple_egl_lease_CFLAGS = $(AM_CFLAGS) $(SIMPLE_EGL_LEASE_CLIENT_CFLAGS) \
+				 $(LIBDRM_CFLAGS)
+weston_simple_egl_lease_LDADD = $(SIMPLE_EGL_LEASE_CLIENT_LIBS) $(LIBDRM_LIBS) \
+				$(GBM_LIBS) -lm
+endif
+
 noinst_LTLIBRARIES += libtoytoolkit.la
 
 libtoytoolkit_la_SOURCES =				\
diff --git a/clients/simple-egl-lease.c b/clients/simple-egl-lease.c
new file mode 100644
index 0000000..8922e57
--- /dev/null
+++ b/clients/simple-egl-lease.c
@@ -0,0 +1,887 @@ 
+/*
+ * Copyright 2018 NXP
+ *
+ * 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 (including the next
+ * paragraph) 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.
+ *
+ * Adapted from kmscube and simple-egl. No atomic support atm.
+ *
+ */
+
+#include "config.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+#include <math.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <wayland-client.h>
+
+#include <GLES2/gl2.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include "drm-lease-unstable-v1-client-protocol.h"
+
+#include <gbm.h>
+#include <drm_fourcc.h>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "simple-egl-lease.h"
+
+static volatile uint8_t sig_recv = 0;
+
+static const GLfloat verts[3][2] = {
+	{ -0.5, -0.5 },
+	{  0.5, -0.5 },
+	{  0,    0.5 }
+};
+
+static const GLfloat colors[3][3] = {
+	{ 1, 0, 0 },
+	{ 0, 1, 0 },
+	{ 0, 0, 1 }
+};
+
+GLfloat rotation[4][4] = {
+	{ 1, 0, 0, 0 },
+	{ 0, 1, 0, 0 },
+	{ 0, 0, 1, 0 },
+	{ 0, 0, 0, 1 }
+};
+
+static const char *vertex_shader_source =
+	"uniform mat4 rotation;\n"
+	"attribute vec4 pos;\n"
+	"attribute vec4 color;\n"
+	"varying vec4 v_color;\n"
+	"void main() {\n"
+	"  gl_Position = rotation * pos;\n"
+	"  v_color = color;\n"
+	"}\n";
+
+static const char *fragment_shader_source =
+	"precision mediump float;\n"
+	"varying vec4 v_color;\n"
+	"void main() {\n"
+	"  gl_FragColor = v_color;\n"
+	"}\n";
+
+static struct gbm *
+init_gbm(int drm_fd, int w, int h)
+{
+	struct gbm *gbm = calloc(1, sizeof(*gbm));
+
+	gbm->dev = gbm_create_device(drm_fd);
+
+	gbm->surface = gbm_surface_create(gbm->dev, w, h, GBM_FORMAT_XRGB8888,
+					  GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+	if (!gbm->surface) {
+		printf("failed to create gbm surface\n");
+		return NULL;
+	}
+
+	gbm->width = w;
+	gbm->height = h;
+
+	return gbm;
+}
+
+static int
+init_egl(struct egl *egl, const struct gbm *gbm)
+{
+	EGLint major, minor, n;
+
+	static const EGLint context_attribs[] = {
+		EGL_CONTEXT_CLIENT_VERSION, 2,
+		EGL_NONE
+	};
+
+	static const EGLint config_attribs[] = {
+		EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+		EGL_RED_SIZE, 1,
+		EGL_GREEN_SIZE, 1,
+		EGL_BLUE_SIZE, 1,
+		EGL_ALPHA_SIZE, 0,
+		EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+		EGL_NONE
+	};
+
+#define get_proc(name) do { \
+		egl->name = (void *)eglGetProcAddress(#name); \
+	} while (0)
+
+	get_proc(eglGetPlatformDisplayEXT);
+
+	if (egl->eglGetPlatformDisplayEXT) {
+		egl->display = egl->eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR,
+				gbm->dev, NULL);
+	} else {
+		egl->display = eglGetDisplay((void *)gbm->dev);
+	}
+
+	if (!eglInitialize(egl->display, &major, &minor)) {
+		printf("failed to initialize\n");
+		return -1;
+	}
+
+	printf("Using display %p with EGL version %d.%d\n",
+			egl->display, major, minor);
+
+	printf("===================================\n");
+	printf("EGL information:\n");
+	printf("  version: \"%s\"\n", eglQueryString(egl->display, EGL_VERSION));
+	printf("  vendor: \"%s\"\n", eglQueryString(egl->display, EGL_VENDOR));
+	printf("  extensions: \"%s\"\n", eglQueryString(egl->display, EGL_EXTENSIONS));
+	printf("===================================\n");
+
+	if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+		printf("failed to bind api EGL_OPENGL_ES_API\n");
+		return -1;
+	}
+
+	if (!eglChooseConfig(egl->display, config_attribs, &egl->config, 1, &n) || n != 1) {
+		printf("failed to choose config: %d\n", n);
+		return -1;
+	}
+
+	egl->context = eglCreateContext(egl->display, egl->config,
+			EGL_NO_CONTEXT, context_attribs);
+	if (egl->context == NULL) {
+		printf("failed to create context\n");
+		return -1;
+	}
+
+	egl->surface = eglCreateWindowSurface(egl->display, egl->config,
+			(EGLNativeWindowType)gbm->surface, NULL);
+	if (egl->surface == EGL_NO_SURFACE) {
+		printf("failed to create egl surface\n");
+		return -1;
+	}
+
+	/* connect the context to the surface */
+	eglMakeCurrent(egl->display, egl->surface, egl->surface, egl->context);
+
+	printf("OpenGL ES 2.x information:\n");
+	printf("  version: \"%s\"\n", glGetString(GL_VERSION));
+	printf("  shading language version: \"%s\"\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
+	printf("  vendor: \"%s\"\n", glGetString(GL_VENDOR));
+	printf("  renderer: \"%s\"\n", glGetString(GL_RENDERER));
+	printf("  extensions: \"%s\"\n", glGetString(GL_EXTENSIONS));
+	printf("===================================\n");
+
+	return 0;
+}
+
+static int
+create_program(const char *vs_src, const char *fs_src)
+{
+	GLuint vertex_shader, fragment_shader, program;
+	GLint ret;
+
+	vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+
+	glShaderSource(vertex_shader, 1, &vs_src, NULL);
+	glCompileShader(vertex_shader);
+
+	glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &ret);
+	if (!ret) {
+		char *log;
+
+		printf("vertex shader compilation failed!:\n");
+		glGetShaderiv(vertex_shader, GL_INFO_LOG_LENGTH, &ret);
+		if (ret > 1) {
+			log = malloc(ret);
+			glGetShaderInfoLog(vertex_shader, ret, NULL, log);
+			printf("%s", log);
+		}
+
+		return -1;
+	}
+
+	fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+
+	glShaderSource(fragment_shader, 1, &fs_src, NULL);
+	glCompileShader(fragment_shader);
+
+	glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &ret);
+	if (!ret) {
+		char *log;
+
+		printf("fragment shader compilation failed!:\n");
+		glGetShaderiv(fragment_shader, GL_INFO_LOG_LENGTH, &ret);
+
+		if (ret > 1) {
+			log = malloc(ret);
+			glGetShaderInfoLog(fragment_shader, ret, NULL, log);
+			printf("%s", log);
+		}
+
+		return -1;
+	}
+
+	program = glCreateProgram();
+
+	glAttachShader(program, vertex_shader);
+	glAttachShader(program, fragment_shader);
+
+	return program;
+}
+
+static int
+link_program(unsigned program)
+{
+	GLint ret;
+
+	glLinkProgram(program);
+
+	glGetProgramiv(program, GL_LINK_STATUS, &ret);
+	if (!ret) {
+		char *log;
+
+		printf("program linking failed!:\n");
+		glGetProgramiv(program, GL_INFO_LOG_LENGTH, &ret);
+
+		if (ret > 1) {
+			log = malloc(ret);
+			glGetProgramInfoLog(program, ret, NULL, log);
+			printf("%s", log);
+		}
+
+		return -1;
+	}
+
+	return 0;
+}
+
+static void
+registry_handle_global(void *data, struct wl_registry *registry,
+		       uint32_t id, const char *interface, uint32_t version)
+{
+	(void) version;
+	struct display *d = data;
+
+	/* test if the interface advertise the leases feat. */
+	if (!strcmp(interface, "zwp_kms_lease_manager_v1")) {
+		d->lease_manager = wl_registry_bind(registry, id,
+				&zwp_kms_lease_manager_v1_interface, 1);
+		fprintf(stdout, "zwp_kms_lease_manager_v1_interface bound\n");
+	}
+}
+
+static void
+registry_handle_global_remove(void *data, struct wl_registry *registry,
+			      uint32_t name)
+{
+	(void) data;
+	(void) name;
+	(void) registry;
+}
+
+
+static const struct wl_registry_listener registry_listener = {
+	registry_handle_global,
+	registry_handle_global_remove
+};
+
+static void lease_created(void *data,
+			  struct zwp_kms_lease_request_v1 *zwp_kms_lease_request_v1,
+			  int32_t fd, uint32_t id)
+{
+	(void) data;
+	(void) zwp_kms_lease_request_v1;
+
+	struct display *display = (struct display *) data;
+	display->drm_display.leased_fd = fd;
+	display->drm_display.lessee_id = id;
+	fprintf(stdout, "Lease created with fd %d, id %u\n", fd, id);
+}
+
+static void lease_failed(void *data,
+			 struct zwp_kms_lease_request_v1 *zwp_kms_lease_request_v1)
+{
+	(void) data;
+	(void) zwp_kms_lease_request_v1;
+
+	fprintf(stdout, "Lease failed\n");
+}
+
+static void lease_revoked(void *data,
+			  struct zwp_kms_lease_request_v1 *zwp_kms_lease_request_v1)
+{
+	(void) data;
+	(void) zwp_kms_lease_request_v1;
+
+	fprintf(stdout, "Lease revoked\n");
+}
+
+static const struct zwp_kms_lease_request_v1_listener lease_request_interface = {
+	lease_created,
+	lease_failed,
+	lease_revoked,
+};
+
+static struct display *
+wl_create_display(void)
+{
+	struct display *display;
+
+	display = calloc(1, sizeof(*display));
+	if (display == NULL) {
+		fprintf(stderr, "out of memory\n");
+		exit(EXIT_FAILURE);
+	}
+	display->display = wl_display_connect(NULL);
+	if (display->display == NULL) {
+		free(display);
+		return NULL;
+	}
+
+	display->registry = wl_display_get_registry(display->display);
+	wl_registry_add_listener(display->registry, &registry_listener, display);
+
+	wl_display_roundtrip(display->display);
+	if (display->lease_manager == NULL) {
+		return NULL;
+	}
+
+	display->lease_request =
+		zwp_kms_lease_manager_v1_create_lease_req(display->lease_manager);
+	zwp_kms_lease_request_v1_add_listener(display->lease_request,
+					      &lease_request_interface,
+					      display);
+
+	wl_display_roundtrip(display->display);
+	return display;
+}
+
+static void
+wl_destroy_display(struct display *display)
+{
+	if (display->lease_manager)
+		zwp_kms_lease_manager_v1_destroy(display->lease_manager);
+
+	wl_display_roundtrip(display->display);
+	wl_registry_destroy(display->registry);
+	wl_display_flush(display->display);
+	wl_display_disconnect(display->display);
+	free(display);
+}
+
+static int
+get_plane_id(uint32_t crtc_index, int drm_fd)
+{
+	drmModePlaneResPtr plane_resources;
+	uint32_t i, j;
+	int ret = -EINVAL;
+	int found_primary = 0;
+
+	plane_resources = drmModeGetPlaneResources(drm_fd);
+	if (!plane_resources) {
+		printf("drmModeGetPlaneResources failed: %s\n", strerror(errno));
+		return -1;
+	}
+
+	for (i = 0; (i < plane_resources->count_planes) && !found_primary; i++) {
+		uint32_t id = plane_resources->planes[i];
+		drmModePlanePtr plane = drmModeGetPlane(drm_fd, id);
+		if (!plane) {
+			printf("drmModeGetPlane(%u) failed: %s\n", id, strerror(errno));
+			continue;
+		}
+
+		if (plane->possible_crtcs & (1 << crtc_index)) {
+			drmModeObjectPropertiesPtr props =
+				drmModeObjectGetProperties(drm_fd, id, DRM_MODE_OBJECT_PLANE);
+
+			/* primary or not, this plane is good enough to use: */
+			ret = id;
+
+			for (j = 0; j < props->count_props; j++) {
+				drmModePropertyPtr p =
+					drmModeGetProperty(drm_fd, props->props[j]);
+
+				if ((strcmp(p->name, "type") == 0) &&
+				    (props->prop_values[j] == DRM_PLANE_TYPE_PRIMARY)) {
+					/* found our primary plane, lets use that: */
+					found_primary = 1;
+				}
+
+				drmModeFreeProperty(p);
+			}
+			drmModeFreeObjectProperties(props);
+		}
+		drmModeFreePlane(plane);
+	}
+	drmModeFreePlaneResources(plane_resources);
+
+	return ret;
+}
+
+static void
+create_wl_lease(struct display *display,
+		const struct drm_resources *drm_resources)
+{
+	zwp_kms_lease_request_v1_add_connector(display->lease_request,
+					       drm_resources->connector_id);
+	zwp_kms_lease_request_v1_add_crtc(display->lease_request,
+					  drm_resources->crtc_id);
+	zwp_kms_lease_request_v1_add_plane(display->lease_request,
+					  drm_resources->plane_id);
+
+	zwp_kms_lease_request_v1_create(display->lease_request);
+
+	wl_display_dispatch(display->display);
+	wl_display_roundtrip(display->display);
+}
+
+static uint32_t
+get_crtc_index(const drmModeRes *resources, uint32_t crtc_id)
+{
+	int i;
+	uint32_t crtc_index = 0;
+
+	for (i = 0; i < resources->count_crtcs; i++) {
+		if (resources->crtcs[i] == crtc_id) {
+			crtc_index = i;
+			break;
+		}
+	}
+
+	return crtc_index;
+}
+
+static int
+get_mode(struct drm_resources *drm_resources, const drmModeConnector *connector)
+{
+	int i, area;
+
+	for (i = 0, area = 0; i < connector->count_modes; i++) {
+		drmModeModeInfo *current_mode = &connector->modes[i];
+
+		if (current_mode->type & DRM_MODE_TYPE_PREFERRED) {
+			drm_resources->mode = current_mode;
+		}
+
+		int current_area = current_mode->hdisplay * current_mode->vdisplay;
+		if (current_area > area) {
+			drm_resources->mode = current_mode;
+			area = current_area;
+		}
+	}
+
+	if (!drm_resources->mode) {
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+check_wl_drm_resources(struct display *display, int drm_fd,
+		       struct drm_resources *drm_resources)
+{
+	drmModeRes *resources = drmModeGetResources(drm_fd);
+	drmModeConnector *connector = NULL;
+	drmModeEncoder *encoder = NULL;
+
+	int i, j;
+	int lease_created = -1;
+
+	/*
+	 * try to determine which of the connectors is OK to use as a lease.
+	 */
+	for (i = 0; i < resources->count_connectors; i++) {
+		connector = drmModeGetConnector(drm_fd, resources->connectors[i]);
+
+		if (connector->connection == DRM_MODE_CONNECTED) {
+			drm_resources->connector_id = connector->connector_id;
+
+			for (j = 0; i < resources->count_encoders; j++) {
+				encoder = drmModeGetEncoder(drm_fd, resources->encoders[j]);
+				if (!encoder)
+					break;
+
+				if (encoder->encoder_id == connector->encoder_id) {
+
+					drm_resources->crtc_id =
+						encoder->crtc_id;
+					drm_resources->crtc_index =
+						get_crtc_index(resources, drm_resources->crtc_id);
+					drm_resources->plane_id =
+						get_plane_id(drm_resources->crtc_index, drm_fd);
+
+					if (get_mode(drm_resources, connector) < 0) {
+						drmModeFreeConnector(connector);
+						drmModeFreeEncoder(encoder);
+						drmModeFreeResources(resources);
+						goto out;
+					}
+
+					create_wl_lease(display, drm_resources);
+					if (display->drm_display.leased_fd <= 0) {
+						drmModeFreeEncoder(encoder);
+						encoder = NULL;
+						break;
+					} else {
+
+						lease_created = 0;
+						drm_resources->drm_fd =
+							display->drm_display.leased_fd;
+
+						drmModeFreeEncoder(encoder);
+						drmModeFreeResources(resources);
+						goto out;
+					}
+				}
+			}
+		}
+
+		drmModeFreeConnector(connector);
+		connector = NULL;
+	}
+
+	drmModeFreeResources(resources);
+
+out:
+	return lease_created;
+}
+
+static void
+drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
+{
+	int drm_fd = gbm_device_get_fd(gbm_bo_get_device(bo));
+	struct drm_fb *fb = data;
+
+	if (fb->fb_id)
+		drmModeRmFB(drm_fd, fb->fb_id);
+
+	free(fb);
+}
+
+static struct drm_fb *
+drm_fb_get_from_bo(struct gbm_bo *bo)
+{
+	int drm_fd = gbm_device_get_fd(gbm_bo_get_device(bo));
+	struct drm_fb *fb = gbm_bo_get_user_data(bo);
+	uint32_t width, height,
+		 strides[4] = {0}, handles[4] = {0},
+		 offsets[4] = {0};
+	int ret = -1;
+
+	if (fb)
+		return fb;
+
+	fb = calloc(1, sizeof *fb);
+	fb->bo = bo;
+
+	width = gbm_bo_get_width(bo);
+	height = gbm_bo_get_height(bo);
+
+
+	memcpy(handles, (uint32_t [4]) { gbm_bo_get_handle(bo).u32,0,0,0 }, 16);
+	memcpy(strides, (uint32_t [4]) { gbm_bo_get_stride(bo),0,0,0 }, 16);
+	memset(offsets, 0, 16);
+
+	ret = drmModeAddFB2(drm_fd, width, height, DRM_FORMAT_XRGB8888,
+			    handles, strides, offsets, &fb->fb_id, 0);
+
+	if (ret) {
+		printf("failed to create fb: %s\n", strerror(errno));
+		free(fb);
+		return NULL;
+	}
+
+	gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
+
+	return fb;
+}
+
+static void
+draw_triangle(struct egl *egl)
+{
+	struct gl *gl = (struct gl *) egl;
+	GLfloat angle;
+	struct timeval tv;
+	uint32_t time;
+
+	gettimeofday(&tv, NULL);
+	time = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+
+	angle = (time / 5) % 360 * M_PI / 180.0;
+	rotation[0][0] =  cos(angle);
+	rotation[0][2] =  sin(angle);
+	rotation[2][0] = -sin(angle);
+	rotation[2][2] =  cos(angle);
+
+	glUniformMatrix4fv(gl->rotation_uniform, 1, GL_FALSE, (GLfloat *) rotation);
+
+	glClearColor(0.0, 0.0, 0.0, 0.5);
+	glClear(GL_COLOR_BUFFER_BIT);
+
+	glVertexAttribPointer(gl->pos, 2, GL_FLOAT, GL_FALSE, 0, verts);
+	glVertexAttribPointer(gl->col, 3, GL_FLOAT, GL_FALSE, 0, colors);
+	glEnableVertexAttribArray(gl->pos);
+	glEnableVertexAttribArray(gl->col);
+
+	glDrawArrays(GL_TRIANGLES, 0, 3);
+
+	glDisableVertexAttribArray(gl->pos);
+	glDisableVertexAttribArray(gl->col);
+}
+
+static struct egl *
+init_triangle(const struct gbm *gbm)
+{
+	int ret;
+	struct gl *gl = calloc(1, sizeof(*gl));
+
+	ret = init_egl(&gl->egl, gbm);
+	if (ret)
+		return NULL;
+
+	gl->aspect = (GLfloat)(gbm->height) / (GLfloat)(gbm->width);
+
+	ret = create_program(vertex_shader_source, fragment_shader_source);
+	if (ret < 0)
+		return NULL;
+
+	gl->program = ret;
+
+	gl->pos = 0;
+	gl->col = 1;
+
+	glBindAttribLocation(gl->program, gl->pos, "pos");
+	glBindAttribLocation(gl->program, gl->col, "color");
+	ret = link_program(gl->program);
+	if (ret)
+		return NULL;
+
+	gl->rotation_uniform = glGetUniformLocation(gl->program, "rotation");
+	glUseProgram(gl->program);
+	glViewport(0, 0, gbm->width, gbm->height);
+
+	gl->egl.draw = draw_triangle;
+	return &gl->egl;
+}
+
+static void
+page_flip_handler(int fd, unsigned int frame,
+		  unsigned int sec, unsigned int usec, void *data)
+{
+	(void) fd;
+	(void) frame;
+	(void) sec;
+	(void) usec;
+
+	int *waiting_for_flip = data;
+	*waiting_for_flip = 0;
+}
+
+static void
+sigint_handler(int sig, siginfo_t *si, void *__unused)
+{
+	(void) sig;
+	(void) si;
+	(void) __unused;
+	sig_recv = 1;
+}
+
+static int
+legacy_run(struct drm_resources *drm, const struct gbm *gbm, struct egl *egl)
+{
+	fd_set fds;
+	drmEventContext evctx = {
+			.version = 2,
+			.page_flip_handler = page_flip_handler,
+	};
+	struct gbm_bo *bo;
+	struct drm_fb *fb;
+	int ret;
+
+	struct sigaction sa;
+
+	sa.sa_flags = SA_SIGINFO;
+	sigemptyset(&sa.sa_mask);
+	sa.sa_sigaction = sigint_handler;
+	sigaction(SIGINT, &sa, NULL);
+
+
+	FD_ZERO(&fds);
+	FD_SET(0, &fds);
+	FD_SET(drm->drm_fd, &fds);
+
+	eglSwapBuffers(egl->display, egl->surface);
+	bo = gbm_surface_lock_front_buffer(gbm->surface);
+	if (!bo) {
+		return -1;
+	}
+
+	fb = drm_fb_get_from_bo(bo);
+	if (!fb) {
+		fprintf(stderr, "Failed to get a new framebuffer BO\n");
+		return -1;
+	}
+
+	fprintf(stdout, "Doing modeset on fd %d, CRCT_ID %d, CONNECTOR_ID %d\n",
+			drm->drm_fd, drm->crtc_id, drm->connector_id);
+
+	/* set mode: */
+	ret = drmModeSetCrtc(drm->drm_fd, drm->crtc_id, fb->fb_id, 0, 0,
+			     &drm->connector_id, 1, drm->mode);
+	if (ret) {
+		printf("failed to set mode: %s\n", strerror(errno));
+		return ret;
+	}
+
+	while (1) {
+		struct gbm_bo *next_bo;
+		int waiting_for_flip = 1;
+
+		if (sig_recv) {
+			break;
+		}
+
+		egl->draw(egl);
+
+		eglSwapBuffers(egl->display, egl->surface);
+		next_bo = gbm_surface_lock_front_buffer(gbm->surface);
+		fb = drm_fb_get_from_bo(next_bo);
+		if (!fb) {
+			fprintf(stderr, "Failed to get a new framebuffer BO\n");
+			return -1;
+		}
+
+		/*
+		 * Here you could also update drm plane layers if you want
+		 * hw composition
+		 */
+
+		ret = drmModePageFlip(drm->drm_fd, drm->crtc_id, fb->fb_id,
+				      DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
+		if (ret) {
+			printf("failed to queue page flip: %s\n", strerror(errno));
+			return -1;
+		}
+
+		while (waiting_for_flip) {
+			ret = select(drm->drm_fd + 1, &fds, NULL, NULL, NULL);
+			if (ret < 0) {
+				printf("select err: %s\n", strerror(errno));
+				/* in case we get the signal in select() */
+				if (errno == EINTR) {
+					gbm_surface_release_buffer(gbm->surface, bo);
+					break;
+				}
+				return ret;
+			} else if (ret == 0) {
+				printf("select timeout!\n");
+				return -1;
+			} else if (FD_ISSET(0, &fds)) {
+				printf("user interrupted!\n");
+				break;
+			}
+			drmHandleEvent(drm->drm_fd, &evctx);
+		}
+
+		/* release last buffer to render on again: */
+		gbm_surface_release_buffer(gbm->surface, bo);
+		bo = next_bo;
+	}
+
+	return 0;
+}
+
+static void
+run(struct drm_resources *drm)
+{
+	struct gbm *gbm;
+	struct egl *egl;
+
+	drm->run = legacy_run;
+
+	gbm = init_gbm(drm->drm_fd, drm->mode->hdisplay, drm->mode->vdisplay);
+	if (!gbm) {
+		printf("failed to initialize GBM\n");
+		return;
+	}
+
+	fprintf(stdout, "gbm @ %p\n", gbm);
+
+	egl = init_triangle(gbm);
+	if (!egl) {
+		printf("failed to initialize EGL\n");
+		return;
+	}
+
+	drm->run(drm, gbm, egl);
+}
+
+int
+main(int argc, char **argv)
+{
+	struct drm_resources drm_resources = {};
+	struct display *display;
+
+	/* FIXME: need to properly detect device */
+	const char *device = "/dev/dri/card0";
+
+	display = wl_create_display();
+	if (!display) {
+		exit(EXIT_FAILURE);
+	}
+
+	int drm_fd = open(device, O_RDWR);
+
+	/*
+	 * this will populate the required info to perform modesetting and
+	 * rendering hence the need to perform again initialization of drm
+	 * is not necessary.
+	 */
+	if (check_wl_drm_resources(display, drm_fd, &drm_resources) < 0) {
+		close(drm_fd);
+		wl_destroy_display(display);
+		fprintf(stdout, "Failed to get a proper lease\n");
+		exit(EXIT_FAILURE);
+	}
+	close(drm_fd);
+
+	run(&drm_resources);
+
+	zwp_kms_lease_request_v1_revoke(display->lease_request,
+					display->drm_display.lessee_id);
+
+	wl_display_dispatch(display->display);
+	wl_display_roundtrip(display->display);
+
+	wl_destroy_display(display);
+	return 0;
+}
diff --git a/clients/simple-egl-lease.h b/clients/simple-egl-lease.h
new file mode 100644
index 0000000..3036276
--- /dev/null
+++ b/clients/simple-egl-lease.h
@@ -0,0 +1,99 @@ 
+/*
+ * Copyright 2018 NXP
+ *
+ * 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 (including the next
+ * paragraph) 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 __SIMPLE_EGL_LEASE_H
+#define __SIMPLE_EGL_LEASE_H
+
+#ifndef EGL_KHR_platform_gbm
+#define EGL_KHR_platform_gbm 1
+#define EGL_PLATFORM_GBM_KHR              0x31D7
+#endif /* EGL_KHR_platform_gbm */
+
+#ifndef EGL_EXT_platform_base
+#define EGL_EXT_platform_base 1
+typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC) (EGLenum platform, void *native_display, const EGLint *attrib_list);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLDisplay EGLAPIENTRY eglGetPlatformDisplayEXT (EGLenum platform, void *native_display, const EGLint *attrib_list);
+#endif
+#endif /* EGL_EXT_platform_base */
+
+struct gbm {
+	struct gbm_device *dev;
+	struct gbm_surface *surface;
+	int width, height;
+};
+
+struct egl {
+	EGLDisplay display;
+	EGLConfig config;
+	EGLContext context;
+	EGLSurface surface;
+
+	PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT;
+	void (*draw)(struct egl *gl);
+};
+
+struct gl {
+	struct egl egl;
+	GLfloat aspect;
+	GLuint program;
+
+	GLuint rotation_uniform;
+	GLuint pos;
+	GLuint col;
+};
+
+struct drm_resources {
+	int drm_fd;
+
+	uint32_t connector_id;
+	uint32_t crtc_id;
+	uint32_t plane_id;
+
+	uint32_t crtc_index;
+	drmModeModeInfo *mode;
+
+	int (*run)(struct drm_resources *drm, const struct gbm *gbm, struct egl *egl);
+};
+
+struct drm_fb {
+	struct gbm_bo *bo;
+	uint32_t fb_id;
+};
+
+struct display {
+	struct wl_display *display;
+	struct wl_registry *registry;
+	struct wl_compositor *compositor;
+
+	struct zwp_kms_lease_manager_v1 *lease_manager;
+	struct zwp_kms_lease_request_v1 *lease_request;
+
+	struct {
+		int leased_fd;
+		uint32_t lessee_id;
+	} drm_display;
+};
+
+
+#endif
diff --git a/configure.ac b/configure.ac
index a81b137..f630898 100644
--- a/configure.ac
+++ b/configure.ac
@@ -383,6 +383,23 @@  if test x$enable_simple_egl_clients = xyes; then
                     [egl glesv2 wayland-client wayland-egl wayland-cursor])
 fi
 
+AC_ARG_ENABLE(simple-egl-lease-clients,
+              AS_HELP_STRING([--disable-simple-egl-lease-clients],
+                             [do not build the simple EGL clients]),,
+              enable_simple_egl_lease_clients="$enable_egl")
+AM_CONDITIONAL(BUILD_SIMPLE_EGL_LEASE_CLIENTS, test "x$enable_simple_egl_lease_clients" = "xyes")
+if test x$enable_simple_egl_lease_clients = xyes; then
+  PKG_CHECK_MODULES(SIMPLE_EGL_LEASE_CLIENT,
+                    [egl glesv2 wayland-client])
+
+  PKG_CHECK_MODULES(DRM_LEASE, [libdrm >= 2.4.89],
+		    [AC_DEFINE([HAVE_DRM_LEASE], 1, [libdrm support lease capability])],
+		    [AC_MSG_WARN([libdrm doesn't have leases support, will omit that capability])])
+  PKG_CHECK_MODULES(GBM, [gbm >= 10.2],
+		    [AC_DEFINE([HAVE_GBM], 1, [gbm support])],
+		    [AC_MSG_WARN([no gbm support])])
+fi
+
 AC_ARG_ENABLE(simple-dmabuf-drm-client,
               AS_HELP_STRING([--disable-simple-dmabuf-drm-client],
                              [do not build the simple dmabuf drm client]),,