7f065bcb2106e44cacdb01f2648365840e67f5bb — Keith Packard 2 years ago 8f5d22b
Add xcb code to get a lease from the X server for our output.

Signed-off-by: Keith Packard <keithp@keithp.com>
8 files changed, 179 insertions(+), 38 deletions(-)

M Makefile.am
M configure.ac
M cube-tex.c
M drm-atomic.c
M drm-common.c
M drm-common.h
M drm-legacy.c
M kmscube.c
M Makefile.am => Makefile.am +2 -0
@@ 26,6 26,7 @@ CFLAGS=-O0 -g
  
  kmscube_LDADD = \
+ 	$(XCB_LIBS) \
  	$(DRM_LIBS) \
  	$(GBM_LIBS) \
  	$(EGL_LIBS) \


@@ 36,6 37,7 @@ kmscube_CFLAGS = \
  	-O0 -g \
  	-Wall -Wextra \
+ 	$(XCB_CFLAGS) \
  	$(DRM_CFLAGS) \
  	$(GBM_CFLAGS) \
  	$(EGL_CFLAGS) \

M configure.ac => configure.ac +1 -0
@@ 39,6 39,7 @@ PKG_CHECK_MODULES(GBM, gbm)
  PKG_CHECK_MODULES(EGL, egl)
  PKG_CHECK_MODULES(GLES2, glesv2)
+ PKG_CHECK_MODULES(XCB, xcb-randr xcb)
  
  AC_CONFIG_FILES([Makefile])
  AC_OUTPUT

M cube-tex.c => cube-tex.c +2 -1
@@ 222,7 222,8 @@ /* NOTE: do not actually use GBM_BO_USE_WRITE since that gets us a dumb buffer: */
  	bo = gbm_bo_create(gl->gbm->dev, texw, texh, GBM_FORMAT_ARGB8888, GBM_BO_USE_LINEAR);
  
- 	map = gbm_bo_map(bo, 0, 0, texw, texh, GBM_BO_TRANSFER_WRITE, &stride, &map_data);
+ 	map = gbm_bo_map(bo, 0, 0, texw, texh, GBM_BO_TRANSFER_WRITE,
+                          &stride, &map_data);
  
  	for (uint32_t i = 0; i < texh; i++) {
  		memcpy(&map[stride * i], &src[texw * 4 * i], texw * 4);

M drm-atomic.c => drm-atomic.c +2 -2
@@ 311,13 311,13 @@ return ret;
  }
  
- struct drm * init_drm_atomic(int fd)
+ struct drm * init_drm_atomic(int fd, int nfd)
  {
  	uint32_t plane_id;
  	int ret;
          struct drm *drm = calloc(1, sizeof (struct drm));
  
- 	ret = init_drm(drm, fd);
+ 	ret = init_drm(drm, fd, nfd);
  	if (ret)
  		return NULL;
  

M drm-common.c => drm-common.c +30 -10
@@ 91,7 91,27 @@ return -1;
  }
  
- static uint32_t find_crtc_for_connector(const struct drm *drm, const drmModeRes *resources,
+ static int leased(int fd, int nfd, int id)
+ {
+         if (fd != nfd && nfd != -1) {
+                 drmModeObjectListPtr    lease = drmModeGetLease(nfd);
+                 int ret = 0;
+ 
+                 if (lease) {
+                         uint32_t        u;
+                         for (u = 0; u < lease->count; u++)
+                                 if (lease->objects[u] == (uint32_t) id) {
+                                         ret = 1;
+                                         break;
+                                 }
+                         free(lease);
+                         return ret;
+                 }
+         }
+         return 0;
+ }
+ 
+ static uint32_t find_crtc_for_connector(int fd, int nfd, const struct drm *drm, const drmModeRes *resources,
  		const drmModeConnector *connector) {
  	int i;
  


@@ 99,7 119,7 @@ const uint32_t encoder_id = connector->encoders[i];
  		drmModeEncoder *encoder = drmModeGetEncoder(drm->fd, encoder_id);
  
- 		if (encoder) {
+ 		if (encoder && !leased(fd, nfd, encoder->encoder_id)) {
  			const uint32_t crtc_id = find_crtc_for_encoder(resources, encoder);
  
  			drmModeFreeEncoder(encoder);


@@ 113,7 133,7 @@ return -1;
  }
  
- int find_drm(struct drm_resources *drm_resources, int fd)
+ int find_drm(struct drm_resources *drm_resources, int fd, int nfd)
  {
  	drmModeRes *resources;
  	drmModeConnector *connector = NULL;


@@ 129,7 149,7 @@ /* find a connected connector: */
  	for (i = 0; i < resources->count_connectors; i++) {
  		connector = drmModeGetConnector(fd, resources->connectors[i]);
- 		if (connector->connection == DRM_MODE_CONNECTED) {
+ 		if (connector->connection == DRM_MODE_CONNECTED && !leased(fd, nfd, connector->connector_id)) {
                          printf("connector id %u\n", connector->connector_id);
  			/* it's connected, let's use this! */
                          drm_resources->connector = connector->connector_id;


@@ 150,7 170,7 @@ /* find encoder: */
  	for (i = 0; i < resources->count_encoders; i++) {
  		encoder = drmModeGetEncoder(fd, resources->encoders[i]);
- 		if (encoder->encoder_id == connector->encoder_id)
+ 		if (encoder->encoder_id == connector->encoder_id && !leased(fd, nfd, encoder->encoder_id))
  			break;
  		drmModeFreeEncoder(encoder);
  		encoder = NULL;


@@ 165,7 185,7 @@ const uint32_t encoder_id = connector->encoders[i];
                          drmModeEncoder *encoder = drmModeGetEncoder(fd, encoder_id);
  
-                         if (encoder) {
+                         if (encoder && !leased(fd, nfd, encoder->encoder_id)) {
                                  crtc_id = find_crtc_for_encoder(resources, encoder);
  
                                  if (crtc_id != 0)


@@ 187,7 207,7 @@ return 0;
  }
  
- int init_drm(struct drm *drm, int fd)
+ int init_drm(struct drm *drm, int fd, int nfd)
  {
  	drmModeRes *resources;
  	drmModeConnector *connector = NULL;


@@ 205,7 225,7 @@ /* find a connected connector: */
  	for (i = 0; i < resources->count_connectors; i++) {
  		connector = drmModeGetConnector(drm->fd, resources->connectors[i]);
- 		if (connector->connection == DRM_MODE_CONNECTED) {
+ 		if (!leased(fd, nfd, connector->connector_id) && connector->connection == DRM_MODE_CONNECTED) {
                          printf("connector id %u\n", connector->connector_id);
  			/* it's connected, let's use this! */
  			break;


@@ 245,7 265,7 @@ /* find encoder: */
  	for (i = 0; i < resources->count_encoders; i++) {
  		encoder = drmModeGetEncoder(drm->fd, resources->encoders[i]);
- 		if (encoder->encoder_id == connector->encoder_id)
+ 		if (!leased(fd, nfd, encoder->encoder_id) && encoder->encoder_id == connector->encoder_id)
  			break;
  		drmModeFreeEncoder(encoder);
  		encoder = NULL;


@@ 254,7 274,7 @@ if (encoder) {
  		drm->crtc_id = encoder->crtc_id;
  	} else {
- 		uint32_t crtc_id = find_crtc_for_connector(drm, resources, connector);
+ 		uint32_t crtc_id = find_crtc_for_connector(fd, nfd, drm, resources, connector);
  		if (crtc_id == 0) {
  			printf("no crtc found!\n");
  			return -1;

M drm-common.h => drm-common.h +4 -4
@@ 73,9 73,9 @@   struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo);
  
- int init_drm(struct drm *drm, int fd);
- struct drm * init_drm_legacy(int fd);
- struct drm * init_drm_atomic(int fd);
+ int init_drm(struct drm *drm, int fd, int nfd);
+ struct drm * init_drm_legacy(int fd, int nfd);
+ struct drm * init_drm_atomic(int fd, int nfd);
  
  struct drm_resources {
      uint32_t    encoder;


@@ 83,6 83,6 @@ uint32_t    crtc;
  };
  
- int find_drm(struct drm_resources *drm_resources, int fd);
+ int find_drm(struct drm_resources *drm_resources, int fd, int nfd);
  
  #endif /* _DRM_COMMON_H */

M drm-legacy.c => drm-legacy.c +2 -2
@@ 112,12 112,12 @@ return 0;
  }
  
- struct drm * init_drm_legacy(int fd)
+ struct drm * init_drm_legacy(int fd, int nfd)
  {
  	int ret;
          struct drm *drm = calloc (1, sizeof (struct drm));
  
- 	ret = init_drm(drm, fd);
+ 	ret = init_drm(drm, fd, nfd);
  	if (ret)
  		return NULL;
  

M kmscube.c => kmscube.c +136 -19
@@ 30,6 30,8 @@ #include <stdio.h>
  #include <getopt.h>
  #include <pthread.h>
+ #include <xcb/xcb.h>
+ #include <xcb/randr.h>
  
  #include "common.h"
  #include "drm-common.h"


@@ 37,7 39,7 @@   #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
  
- static const char shortopts[] = "ArD:M:";
+ static const char shortopts[] = "ArD:M:l";
  
  static enum mode mode = SMOOTH;
  static int atomic = 0;


@@ 47,6 49,7 @@ {"device", required_argument, 0, 'D'},
  	{"mode",   required_argument, 0, 'M'},
          {"thread", no_argument,       0, 't'},
+         {"lease", no_argument,        0, 'l'},
  	{0, 0, 0, 0}
  };
  


@@ 58,6 61,7 @@ "    -A, --atomic             use atomic modesetting and fencing\n"
  			"    -D, --device=DEVICE      use the given device\n"
                 		"    -t, --thread             two threads and cubes on two monitors\n"
+                         "    -l, --lease              lease output from the X server\n"
  			"    -M, --mode=MODE          specify mode, one of:\n"
  			"        smooth    -  smooth shaded cube (default)\n"
  			"        rgba      -  rgba textured cube\n"


@@ 90,16 94,16 @@ printf("\n");
  }
  
- int run(int fd) {
+ int run(int fd, int nfd) {
          struct egl *egl;
          struct gbm *gbm;
          struct drm *drm;
          int ret;
  
  	if (atomic)
- 		drm = init_drm_atomic(fd);
+                 drm = init_drm_atomic(fd, nfd);
  	else
- 		drm = init_drm_legacy(fd);
+                 drm = init_drm_legacy(fd, nfd);
  	if (!drm) {
  		printf("failed to initialize %s DRM\n", atomic ? "atomic" : "legacy");
  		return -1;


@@ 137,7 141,7 @@       printf("child on fd %d\n", fd);
      printf("child running now...\n");
-     run(fd);
+     run(fd, -1);
      return 0;
  }
  


@@ 145,8 149,10 @@ {
  	const char *device = "/dev/dri/card0";
          int do_thread = 0;
+         int do_lease = 0;
  	int opt;
          int fd;
+         int nfd = -1;
  
  	while ((opt = getopt_long_only(argc, argv, shortopts, longopts, NULL)) != -1) {
  		switch (opt) {


@@ 174,29 180,140 @@ case 't':
                          do_thread = 1;
                          break;
+                 case 'l':
+                         do_lease = 1;
+                         break;
  		default:
  			usage(argv[0]);
  			return -1;
  		}
  	}
  
-         fd = open(device, O_RDWR);
-         if (fd < 0) {
-                 printf("could not open drm device\n");
-                 return -1;
+         if (do_lease) {
+                 xcb_connection_t        *connection;
+                 int                     screen;
+ 
+                 connection = xcb_connect(NULL, &screen);
+                 if (!connection) {
+                         printf("Connection to X server failed\n");
+                         exit(1);
+                 }
+                 xcb_randr_query_version_cookie_t rqv_c = xcb_randr_query_version(connection,
+                                                                                  XCB_RANDR_MAJOR_VERSION,
+                                                                                  XCB_RANDR_MINOR_VERSION);
+                 xcb_randr_query_version_reply_t *rqv_r = xcb_randr_query_version_reply(connection, rqv_c, NULL);
+ 
+                 if (!rqv_r || rqv_r->minor_version < 6) {
+                         printf("No new-enough RandR version\n");
+                         exit(1);
+                 }
+ 
+                 xcb_screen_iterator_t s_i;
+ 
+                 int i_s = 0;
+ 
+                 for (s_i = xcb_setup_roots_iterator(xcb_get_setup(connection));
+                      s_i.rem;
+                      xcb_screen_next(&s_i), i_s++) {
+                         printf ("index %d screen %d\n", s_i.index, screen);
+                         if (i_s == screen)
+                                 break;
+                 }
+ 
+                 xcb_window_t root = s_i.data->root;
+ 
+                 printf("root %x\n", root);
+ 
+                 xcb_randr_get_screen_resources_cookie_t gsr_c = xcb_randr_get_screen_resources(connection, root);
+ 
+                 xcb_randr_get_screen_resources_reply_t *gsr_r = xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL);
+ 
+                 if (!gsr_r) {
+                         printf("get_screen_resources failed\n");
+                         exit(1);
+                 }
+ 
+                 xcb_randr_output_t *ro = xcb_randr_get_screen_resources_outputs(gsr_r);
+                 int o, c;
+ 
+                 xcb_randr_output_t output = 0;
+ 
+                 /* Find a connected but idle output */
+                 for (o = 0; output == 0 && o < gsr_r->num_outputs; o++) {
+                         xcb_randr_get_output_info_cookie_t goi_c = xcb_randr_get_output_info(connection, ro[o], gsr_r->config_timestamp);
+ 
+                         xcb_randr_get_output_info_reply_t *goi_r = xcb_randr_get_output_info_reply(connection, goi_c, NULL);
+ 
+                         /* Find the first connected but unused output */
+                         if (goi_r->connection == XCB_RANDR_CONNECTION_CONNECTED &&
+                             goi_r->crtc == 0) {
+                                 output = ro[o];
+                         }
+ 
+                         free(goi_r);
+                 }
+ 
+                 xcb_randr_crtc_t *rc = xcb_randr_get_screen_resources_crtcs(gsr_r);
+ 
+                 xcb_randr_crtc_t crtc = 0;
+ 
+                 /* Find an idle crtc */
+                 for (c = 0; crtc == 0 && c < gsr_r->num_crtcs; c++) {
+                         xcb_randr_get_crtc_info_cookie_t gci_c = xcb_randr_get_crtc_info(connection, rc[c], gsr_r->config_timestamp);
+ 
+                         xcb_randr_get_crtc_info_reply_t *gci_r = xcb_randr_get_crtc_info_reply(connection, gci_c, NULL);
+ 
+                         /* Find the first connected but unused crtc */
+                         if (gci_r->mode == 0)
+                                 crtc = rc[c];
+ 
+                         free(gci_r);
+                 }
+ 
+                 free(gsr_r);
+ 
+                 printf("output %x crtc %x\n", output, crtc);
+ 
+                 xcb_randr_lease_t lease = xcb_generate_id(connection);
+ 
+                 xcb_randr_create_lease_cookie_t rcl_c = xcb_randr_create_lease(connection,
+                                                                                root,
+                                                                                lease,
+                                                                                1,
+                                                                                1,
+                                                                                &crtc,
+                                                                                &output);
+                 xcb_randr_create_lease_reply_t *rcl_r = xcb_randr_create_lease_reply(connection, rcl_c, NULL);
+ 
+                 if (!rcl_r) {
+                         printf("create_lease failed\n");
+                         exit(1);
+                 }
+ 
+                 int *rcl_f = xcb_randr_create_lease_reply_fds(connection, rcl_r);
+ 
+                 fd = rcl_f[0];
+ 
+                 printf("fd %d\n", fd);
+ 
+         } else {
+                 fd = open(device, O_RDWR);
+                 if (fd < 0) {
+                         printf("could not open drm device\n");
+                         return -1;
+                 }
          }
          if (do_thread) {
                  struct drm_resources resources;
                  uint32_t objects[3];
                  int nobjects;
-                 int nfd;
                  uint32_t lessee;
                  pthread_t thread;
  
                  drmModeLesseeListPtr l_list;
                  drmModeObjectListPtr o_list;
  
-                 if (find_drm(&resources, fd) != 0) {
+                 if (find_drm(&resources, fd, -1) != 0) {
                          printf("Could not get DRM resources for fork\n");
                          return -1;
                  }


@@ 211,36 328,36 @@ if (resources.crtc)
                          objects[nobjects++] = resources.crtc;
  
-                 o_list = drmModeGetLease(fd, 0);
+                 o_list = drmModeGetLease(fd);
  
                  dump_objects("owner before lease", o_list);
                  free(o_list);
  
-                 l_list = drmModeListLessees(fd, 0);
+                 l_list = drmModeListLessees(fd);
  
                  dump_lessees("owner before lease", l_list);
  
                  free(l_list);
  
-                 nfd = drmModeCreateLease(fd, objects, nobjects, 0, 1, &lessee);
+                 nfd = drmModeCreateLease(fd, objects, nobjects, 0, &lessee);
  
-                 o_list = drmModeGetLease(fd, 0);
+                 o_list = drmModeGetLease(fd);
  
                  dump_objects("owner after lease", o_list);
                  free(o_list);
  
-                 l_list = drmModeListLessees(fd, 0);
+                 l_list = drmModeListLessees(fd);
  
                  dump_lessees("owner after lease", l_list);
  
                  free(l_list);
  
-                 o_list = drmModeGetLease(nfd, lessee);
+                 o_list = drmModeGetLease(nfd);
  
                  dump_objects("lessee after lease", o_list);
                  free(o_list);
  
-                 l_list = drmModeListLessees(nfd, lessee);
+                 l_list = drmModeListLessees(nfd);
  
                  dump_lessees("lessee after lease", l_list);
  


@@ 259,5 376,5 @@ printf("parent running now...\n");
          }
  
-         return run(fd);
+         return run(fd, nfd);
  }