/* 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; }