aboutsummaryrefslogtreecommitdiffstats
path: root/libgamma_x_randr_partition_initialise.c
blob: 5c88eb6035651ed054dad1616f3e5dd65655150e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/* 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   nelem  The number of elements in the memory area
 * @param   size   The size, in bytes, of each element
 * @return         A duplication of the memory, `NULL` if zero-length or on error
 */
static inline void *
duparray(void *restrict ptr, size_t nelem, size_t size)
{
	char *restrict rc;
	size_t bytes;
	if (nelem > SIZE_MAX / size) {
		errno = ENOMEM;
		return NULL;
	}
	bytes = nelem * size;
	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(struct libgamma_partition_state *restrict this,
                                      struct libgamma_site_state *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;
	struct libgamma_x_randr_partition_data *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(*data));
	if (!data)
		goto fail;

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

	/* Copy the outputs as well */
	data->outputs = duparray(outputs, (size_t)reply->num_outputs, sizeof(*outputs));
	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) */
	if (reply->num_crtcs) {
		if (reply->num_crtcs > SIZE_MAX / sizeof(*data->crtc_to_output)) {
			errno = ENOMEM;
			goto fail;
		}
		data->crtc_to_output = malloc((size_t)reply->num_crtcs * sizeof(*data->crtc_to_output));
		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;
}