aboutsummaryrefslogblamecommitdiffstats
path: root/libgamma_x_randr_partition_initialise.c
blob: aa5df50ed25a5c1f651814e2be0d7d8ec8c1993f (plain) (tree)
































































































































































                                                                                                                         
/* See LICENSE file for copyright and license details. */
#define IN_LIBGAMMA_X_RANDR
#include "common.h"


/**
 * Duplicate a memory area
 * 
 * @param   ptr    The memory area
 * @param   bytes  The size, in bytes, of the memory area
 * @return         A duplication of the memory, `NULL` if zero-length or on error
 */
static inline void *
xmemdup(void *restrict ptr, size_t bytes)
{
	char *restrict rc;
	if (!bytes)
		return NULL;
	rc = malloc(bytes);
	if (!rc)
		return NULL;
	memcpy(rc, ptr, bytes);
	return rc;
}


/**
 * Initialise an allocated partition state
 * 
 * @param   this       The partition state to initialise
 * @param   site       The site state for the site that the partition belongs to.
 * @param   partition  The the index of the partition within the site
 * @return             Zero on success, otherwise (negative) the value of an
 *                     error identifier provided by this library
 */
int
libgamma_x_randr_partition_initialise(libgamma_partition_state_t *restrict this,
                                      libgamma_site_state_t *restrict site, size_t partition)
{
	int fail_rc = LIBGAMMA_ERRNO_SET;
	xcb_connection_t *restrict connection = site->data;
	xcb_screen_t *restrict screen = NULL;
	xcb_generic_error_t *error = NULL;
	const xcb_setup_t *restrict setup;
	xcb_screen_iterator_t iter;
	xcb_randr_get_screen_resources_current_cookie_t cookie;
	xcb_randr_get_screen_resources_current_reply_t *restrict reply;
	xcb_randr_crtc_t *restrict crtcs;
	xcb_randr_output_t *restrict outputs;
	libgamma_x_randr_partition_data_t *restrict data;
	xcb_randr_get_output_info_cookie_t out_cookie;
	xcb_randr_get_output_info_reply_t *out_reply;
	size_t i;
	uint16_t j;

	/* Get screen list */
	setup = xcb_get_setup(connection);
	if (!setup)
		return LIBGAMMA_LIST_PARTITIONS_FAILED;
	iter = xcb_setup_roots_iterator(setup);

	/* Get the screen */
	for (i = 0; iter.rem > 0; i++, xcb_screen_next(&iter))
		if (i == partition) {
			screen = iter.data;
			break;
		}
	/* Report failure if we did not find the screen */
	if (!iter.rem)
		return LIBGAMMA_NO_SUCH_PARTITION;

	/* Check that the screen is not `NULL`.
	 * (Do not think this can happen, but why not.) */
	if (!screen)
		return LIBGAMMA_NULL_PARTITION;

	/* Get the current resources of the screen */
	cookie = xcb_randr_get_screen_resources_current(connection, screen->root);
	reply = xcb_randr_get_screen_resources_current_reply(connection, cookie, &error);
	if (error)
		return libgamma_x_randr_internal_translate_error(error->error_code, LIBGAMMA_LIST_CRTCS_FAILED, 0);

	/* Get the number of available CRTC:s */
	this->crtcs_available = reply->num_crtcs;
	/* Get the CRTC and output lists */
	crtcs = xcb_randr_get_screen_resources_current_crtcs(reply);
	outputs = xcb_randr_get_screen_resources_current_outputs(reply);
	if (!crtcs || !outputs) {
		free(reply);
		return LIBGAMMA_REPLY_VALUE_EXTRACTION_FAILED;
	}

	/* Allocate adjustment method dependent data memory area.
	   We use `calloc` because we want `data`'s pointers to be `NULL` if not allocated at `fail`. */
	data = calloc(1, sizeof(libgamma_x_randr_partition_data_t));
	if (!data)
		goto fail;

	/* Copy the CRTC:s, just so we do not have to keep the reply in memory */
	data->crtcs = xmemdup(crtcs, (size_t)(reply->num_crtcs) * sizeof(xcb_randr_crtc_t));
	if (!data->crtcs && reply->num_crtcs > 0)
		goto fail;

	/* Copy the outputs as well */
	data->outputs = xmemdup(outputs, (size_t)reply->num_outputs * sizeof(xcb_randr_output_t));
	if (!data->outputs && reply->num_outputs > 0)
		goto fail;

	/* Get the number of available outputs */
	data->outputs_count = (size_t)reply->num_outputs;

	/* Create mapping table from CRTC indices to output indicies. (injection) */
	data->crtc_to_output = malloc((size_t)reply->num_crtcs * sizeof(size_t));
	if (!data->crtc_to_output)
		goto fail;
	/* All CRTC:s should be mapped, but incase they are not, all unmapped CRTC:s should have
	   an invalid target, namely `SIZE_MAX`, which is 1 more than the theoretical limit */
	for (i = 0; i < (size_t)reply->num_crtcs; i++)
		data->crtc_to_output[i] = SIZE_MAX;
	/* Fill the table */
	for (i = 0; i < (size_t)reply->num_outputs; i++) {
		/* Query output (target) information */
		out_cookie = xcb_randr_get_output_info(connection, outputs[i], reply->config_timestamp);
		out_reply = xcb_randr_get_output_info_reply(connection, out_cookie, &error);
		if (error) {
			fail_rc = libgamma_x_randr_internal_translate_error(error->error_code,
			                                                    LIBGAMMA_OUTPUT_INFORMATION_QUERY_FAILED, 0);
			goto fail;
		}

		/* Find CRTC (source) */
		for (j = 0; j < reply->num_crtcs; j++) {
			if (crtcs[j] == out_reply->crtc) {
				data->crtc_to_output[j] = i;
				break;
			}
		}

		/* Release output information */
		free(out_reply);
	}

	/* Store the configuration timestamp */
	data->config_timestamp = reply->config_timestamp;
	/* Store the adjustment method dependent data */
	this->data = data;
	/* Release resources and return successfully */
	free(reply);
	return 0;

fail:
	/* Release resources and return with an error */
	if (data) {
		free(data->crtcs);
		free(data->outputs);
		free(data->crtc_to_output);
		free(data);
	}
	free(reply);
	return fail_rc;
}