[X2Go-Commits] [nx-libs] 03/11: nx-X11/programs/Xserver/hw/nxagent/Screen.c: implement basic screen boxes extending algorithm, generating a list of solutions.
git-admin at x2go.org
git-admin at x2go.org
Sun May 13 06:04:38 CEST 2018
This is an automated email from the git hooks/post-receive script.
x2go pushed a commit to branch 3.6.x-rpm-debug
in repository nx-libs.
commit e696265171533addee3988254b72e3e3d2b4ee46
Author: Mihai Moldovan <ionic at ionic.de>
Date: Thu Mar 29 00:30:48 2018 +0200
nx-X11/programs/Xserver/hw/nxagent/Screen.c: implement basic screen boxes extending algorithm, generating a list of solutions.
---
nx-X11/programs/Xserver/hw/nxagent/Screen.c | 1228 +++++++++++++++++++++++++++
1 file changed, 1228 insertions(+)
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Screen.c b/nx-X11/programs/Xserver/hw/nxagent/Screen.c
index fd344b6..3c7c487 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Screen.c
+++ b/nx-X11/programs/Xserver/hw/nxagent/Screen.c
@@ -5529,6 +5529,1234 @@ static nxagentScreenCrtcsSolutions* nxagentScreenCrtcsGenerateSolutionsSingleScr
}
/*
+ * Helper calculating an obsolete boxes count, given a list of all boxes.
+ *
+ * Note that it might be zero if there are no obsolete boxes or an error
+ * happened. If the error parameter is non-NULL, its value will be set to true
+ * to indicate an error.
+ */
+#ifdef DEBUG
+/*
+ * When debugging, make sure the guards are not optimized away. Otherwise
+ * providing NULL for the err parameter will crash when calling the function
+ * directly in debuggers.
+ */
+__attribute__((optimize("O0")))
+#endif
+static size_t nxagentScreenBoxesObsoleteCount(const nxagentScreenBoxes *all_boxes, Bool *err) {
+ size_t ret = 0;
+
+ if (err) {
+ *err = FALSE;
+ }
+
+ if (!(all_boxes)) {
+ if (err) {
+ *err = TRUE;
+ }
+
+ return(ret);
+ }
+
+ nxagentScreenBoxesElem *cur = NULL;
+ xorg_list_for_each_entry(cur, &(all_boxes->head), entry) {
+ if (cur->obsolete) {
+ ++ret;
+ }
+ }
+
+ return(ret);
+}
+
+/*
+ * Helper that exchanges a screen box for a new one.
+ * The original box is destroyed, the new box is deep-copied.
+ *
+ * Returns true on success, otherwise false.
+ */
+static Bool nxagentScreenBoxesUpdateScreenBox(nxagentScreenBoxes *boxes, const size_t pos, const nxagentScreenBoxesElem *new_box) {
+ Bool ret = FALSE;
+
+ if ((!(boxes)) || (!(new_box))) {
+ return(ret);
+ }
+
+ nxagentScreenBoxesElem *cur_box = NULL;
+ size_t i = 0;
+ xorg_list_for_each_entry(cur_box, &(boxes->head), entry) {
+ if (pos == (i++)) {
+ /* Need to exchange the current box. */
+ nxagentScreenBoxesElem *new_copy = nxagentScreenBoxesElemCopy(new_box, TRUE);
+
+ if (!(new_copy)) {
+ return(ret);
+ }
+
+ /*
+ * Taking out the magic wand here:
+ * Since an xorg_list is cyclic and we know that the current element
+ * exists, we can use an append operation to insert the new element and
+ * then delete the old one.
+ * Instead of appending to the original list head (in this case boxes),
+ * we'll append to the current element. The actual magic is that a list
+ * append operation is *actually* a prepend operation relative to the
+ * passed head element.
+ *
+ * Example:
+ * Original list (each edge is actually a double edge pointing in both
+ * directions):
+ *
+ * ┌──────────────────────────────────────────┐
+ * ↓ │
+ * [HEAD] ↔ [elem1] ↔ [elem2] ↔ [elem3] ↔ ... ←─┘
+ *
+ * Append operation relative to HEAD:
+ *
+ * ┌───────────────────────────────────────────────────────┐
+ * ↓ │
+ * [HEAD] ↔ [elem1] ↔ [elem2] ↔ [elem3] ↔ ... ↔ [new_elem] ←─┘
+ *
+ * Append operation relative to elem2:
+ *
+ * ┌───────────────────────────────────────────────────────┐
+ * ↓ │
+ * [HEAD] ↔ [elem1] ↔ [new_elem] ↔ [elem2] ↔ [elem3] ↔ ... ←─┘
+ *
+ * Afterwards, delete operation on elem2:
+ *
+ * ┌─────────────────────────────────────────────┐
+ * ↓ │
+ * [HEAD] ↔ [elem1] ↔ [new_elem] ↔ [elem3] ↔ ... ←─┘
+ *
+ * Observant readers might have noticed that using either a list append
+ * or add operation (i.e., relative to given head element an inverse
+ * append or "real" append operation) followed by a delete operation for
+ * the current element will lead to the same result.
+ *
+ * We'll use an append/inverse append operation, though, since this makes
+ * the most sense.
+ */
+ xorg_list_append(&(new_copy->entry), &(cur_box->entry));
+ xorg_list_del(&(cur_box->entry));
+
+ /* Get rid of cur_box. */
+ SAFE_FREE(cur_box);
+
+ ret = TRUE;
+ break;
+ }
+ }
+
+ /*
+ * If ret is still false here it means that the list did not contain an
+ * element at position pos (i.e., it was too small).
+ *
+ * No need for special treatment.
+ */
+
+ return(ret);
+}
+
+/*
+ * Helper that generates an array with solution lists for each screen box and
+ * direction.
+ *
+ * The array will always be screen_count-sized.
+ *
+ * Returns a pointer to the array. Will be NULL on failure.
+ */
+static nxagentScreenCrtcsSolutions** nxagentScreenCrtcsGeneratePotentialSolutionArray(const nxagentScreenBoxes *all_boxes, const nxagentScreenBoxes *screen_boxes, const size_t screen_count) {
+ nxagentScreenCrtcsSolutions **ret = NULL;
+
+ /* FIXME: xorg_list_is_empty is not const-correct. */
+ if ((!(screen_count)) || (xorg_list_is_empty((struct xorg_list *)(&(screen_boxes->head)))) || (xorg_list_is_empty((struct xorg_list *)(&(all_boxes->head))))) {
+ return(ret);
+ }
+
+ ret = calloc(screen_count, sizeof(nxagentScreenCrtcsSolutions*));
+
+ if (!(ret)) {
+ return(ret);
+ }
+
+ for (size_t i = 0; i < screen_count; ++i) {
+ nxagentScreenBoxesElem *tmp_box = NULL;
+ {
+ size_t y = 0;
+ xorg_list_for_each_entry(tmp_box, &(screen_boxes->head), entry) {
+ /* y == i means that we found our current box and can break out. */
+ if (y++ == i) {
+ break;
+ }
+ }
+
+ if ((&(tmp_box->entry)) == &(screen_boxes->head)) {
+#ifdef WARNING
+ fprintf(stderr, "%s: reached end of list while fetching specific box. Algorithm error.\n", __func__);
+#endif
+
+ for (size_t z = 0; z < screen_count; ++z) {
+ nxagentScreenCrtcsFreeSolutions(ret[z]);
+
+ SAFE_FREE(ret[z]);
+ }
+
+ SAFE_FREE(ret);
+
+ return(ret);
+ }
+ }
+
+ /* Build other_screens list. */
+ nxagentScreenBoxes *other_screens = calloc(1, sizeof(nxagentScreenBoxes));
+
+ if (!(other_screens)) {
+#ifdef WARNING
+ fprintf(stderr, "%s: unable to allocate space for other_screens list.\n", __func__);
+#endif
+
+ for (size_t y = 0; y < screen_count; ++y) {
+ nxagentScreenCrtcsFreeSolutions(ret[y]);
+
+ SAFE_FREE(ret[y]);
+ }
+
+ SAFE_FREE(ret);
+
+ return(ret);
+ }
+
+ xorg_list_init(&(other_screens->head));
+ other_screens->screen_id = -1;
+
+ nxagentScreenBoxesElem *tmp = NULL;
+ xorg_list_for_each_entry(tmp, &(screen_boxes->head), entry) {
+ if (tmp != tmp_box) {
+ /* Copy current element. */
+ nxagentScreenBoxesElem *box_copy = nxagentScreenBoxesElemCopy(tmp, TRUE);
+
+ if (!(box_copy)) {
+#ifdef WARNING
+ fprintf(stderr, "%s: unable to copy current screen box.\n", __func__);
+#endif
+
+ for (size_t y = 0; y < screen_count; ++y) {
+ nxagentScreenCrtcsFreeSolutions(ret[y]);
+
+ SAFE_FREE(ret[y]);
+ }
+
+ nxagentFreeScreenBoxes(other_screens, TRUE);
+
+ SAFE_FREE(other_screens);
+
+ SAFE_FREE(ret);
+
+ return(ret);
+ }
+
+ /* Add to other_screens list. */
+ xorg_list_append(&(box_copy->entry), &(other_screens->head));
+ }
+ }
+
+ /*
+ * Right now, all_boxes contains all boxes, including obsolete
+ * information, tmp_box is the current screen box to extend and
+ * other_screens contains the other screen boxes.
+ *
+ * With that, we can fetch a solutions list comprising of the best(!)
+ * solutions for extending the current box in all directions.
+ */
+ nxagentScreenCrtcsSolutions *cur_screen_solutions = nxagentScreenCrtcsGenerateSolutionsSingleScreen(all_boxes, tmp_box, other_screens);
+
+ /*
+ * Clean up other_screens. Doing that now means we don't have to do it in
+ * the error handling.
+ */
+ nxagentFreeScreenBoxes(other_screens, TRUE);
+
+ SAFE_FREE(other_screens);
+
+ /* NULL means failure or no solutions. */
+ if (!(cur_screen_solutions)) {
+#ifdef WARNING
+ fprintf(stderr, "%s: no solution found for current configuration. Algorithm error.\n", __func__);
+#endif
+
+ for (size_t y = 0; y < screen_count; ++y) {
+ nxagentScreenCrtcsFreeSolutions(ret[y]);
+
+ SAFE_FREE(ret[y]);
+ }
+
+ SAFE_FREE(ret);
+
+ return(ret);
+ }
+
+ /*
+ * If everything worked out, we'll have a solutions list.
+ * It might contain multiple entries, but at this point we don't care,
+ * since they may not have the highest overall rating.
+ * Just add them to our general solutions list.
+ *
+ * We're just setting plain pointer values here, which should work fine
+ * since the next and prev pointers point to the addresses of an
+ * element's entry xorg_list struct.
+ *
+ * Be careful, though.
+ */
+ ret[i] = cur_screen_solutions;
+ }
+
+ return(ret);
+}
+
+/*
+ * Helper that extracts the best solutions from a screen_count-sized solutions
+ * array and, at the same time, records the per-screen ratings.
+ *
+ * The arrays will always likewise be screen_count-sized.
+ *
+ * The last three parameters are output parameters that expect to be passed a
+ * valid address. NULL pointers for any parameter or a zero screen count will be
+ * treated as an error. init might be zero (i.e., false).
+ *
+ * Returns true on success, otherwise false.
+ */
+static Bool nxagentScreenCrtcsFilterScreenSolutions(const nxagentScreenCrtcsSolutions * const *solutions, const size_t screen_count, const Bool init, const Bool *screens_init, nxagentScreenCrtcsSolutions ***best_screen_solutions, double **screen_ratings, double *max_rating) {
+ Bool ret = FALSE;
+
+ if ((!(solutions)) || (!(screen_count)) || (!(screens_init)) || (!(best_screen_solutions)) || (!(screen_ratings)) || (!(max_rating))) {
+ return(ret);
+ }
+
+ (*screen_ratings) = calloc(screen_count, sizeof(double));
+ (*best_screen_solutions) = calloc(screen_count, sizeof(nxagentScreenCrtcsSolutions*));
+
+ if ((!((*screen_ratings))) || (!((*best_screen_solutions)))) {
+ /* Let caller handle the cleanup. It'll be done there anyway. */
+ return(ret);
+ }
+
+ (*max_rating) = INVALID_RATING;
+ for (size_t i = 0; i < screen_count; ++i) {
+ /* Initialize to lowest (and invalid) value. */
+ (*screen_ratings)[i] = (*max_rating);
+
+ /*
+ * The initial step is slightly different, since we need to consider
+ * every best solution per initial box.
+ * This ensures initial expansion (together with the rating function
+ * favoring initial expansion).
+ *
+ * For this to work, we have to consider all solutions to non-extended
+ * screen boxes. Hence, mixing all solutions into one list and extracting
+ * the best solutions is not feasible in this algorithmic branch since we
+ * wouldn't be able to map the solution back to an initial screen.
+ *
+ * After initialization (i.e., initial screen extension), we have to
+ * consider every screen, not just the non-extended ones.
+ *
+ * Extract the best solutions for each screen and compare the rating
+ * values.
+ */
+ if ((init) || (!(screens_init[i]))) {
+ (*best_screen_solutions)[i] = nxagentScreenCrtcsExtractBestSolutions(solutions[i]);
+
+ if ((!((*best_screen_solutions)[i])) || (xorg_list_is_empty((*best_screen_solutions)[i]))) {
+ /* Let caller handle the cleanup. It'll be done there anyway. */
+ return(ret);
+ }
+
+ nxagentScreenCrtcsSolution *cur = xorg_list_first_entry((*best_screen_solutions)[i], nxagentScreenCrtcsSolution, entry);
+
+ (*screen_ratings)[i] = cur->rating;
+ }
+ }
+
+ for (size_t i = 0; i < screen_count; ++i) {
+ if ((*screen_ratings)[i] > (*max_rating)) {
+ (*max_rating) = (*screen_ratings)[i];
+ }
+ }
+
+ if (INVALID_RATING == (*max_rating)) {
+#ifdef WARNING
+ fprintf(stderr, "%s: no solution found for current configuration in %sscreen extension run%s. Algorithm error.\n", __func__, init ? "initial " : "", init ? ", but not all initial screen boxes have been extended yet" : "");
+#endif
+
+ /* Let caller handle the cleanup. It'll be done there anyway. */
+ return(ret);
+ }
+
+ ret = TRUE;
+
+ return(ret);
+}
+
+/*
+ * Helper setting up its my_solution output parameter according to the first
+ * solution in the best_screen_solutions list.
+ *
+ * No pointer parameters may be NULL. screen_number is allowed to be zero
+ * (indicating the first screen).
+ *
+ * Returns true on success, otherwise false.
+ */
+static Bool nxagentScreenCrtcsSelectSolution(const nxagentScreenBoxes *screen_boxes, const size_t screen_number, const Bool *screens_init, const nxagentScreenCrtcsSolutions *best_screen_solutions, nxagentScreenCrtcsSolution **my_solution) {
+ Bool ret = FALSE;
+
+ if ((!(screen_boxes)) || (!(screens_init)) || (!(best_screen_solutions)) || (!(my_solution))) {
+ return(ret);
+ }
+
+ /*
+ * Always take the first entry for the current run.
+ */
+ nxagentScreenCrtcsSolution *first_entry = xorg_list_first_entry(best_screen_solutions, nxagentScreenCrtcsSolution, entry);
+
+ /*
+ * Assert that first_entry is always a legit one - since we checked
+ * the amount of solutions before, that should be safe.
+ * This is why we don't check the return value here.
+ */
+
+ nxagentScreenCrtcsSolution *tmp_solution = nxagentScreenCrtcsSolutionCopy(first_entry);
+
+ if (!(tmp_solution)) {
+#ifdef WARNING
+ fprintf(stderr, "%s: unable to copy generated solution.\n", __func__);
+#endif
+
+ return(ret);
+ }
+
+ if (!(*my_solution)) {
+ /* If we don't have a solution yet, use this one. */
+ (*my_solution) = tmp_solution;
+ }
+ else {
+ /* Otherwise, modify my_solution. */
+ (*my_solution)->rating_size_change += tmp_solution->rating_size_change;
+ (*my_solution)->rating_cover_penalty += tmp_solution->rating_cover_penalty;
+
+ if (!(screens_init[screen_number])) {
+ (*my_solution)->rating_extended_boxes_count += tmp_solution->rating_extended_boxes_count; /* Should always be +1. */
+ }
+
+ (*my_solution)->rating += tmp_solution->rating;
+
+ /* Plainly take the all_boxes pointer. */
+ nxagentFreeScreenBoxes((*my_solution)->all_boxes, TRUE);
+ SAFE_FREE((*my_solution)->all_boxes);
+ (*my_solution)->all_boxes = nxagentScreenBoxesCopy(tmp_solution->all_boxes);
+
+ if (!((*my_solution)->all_boxes)) {
+#ifdef WARNING
+ fprintf(stderr, "%s: unable to copy current all boxes state.\n", __func__);
+#endif
+
+ return(ret);
+ }
+ }
+
+ /*
+ * The solution boxes list handling is more complicated.
+ * Our new, temporary solution only has the extended screen box
+ * in its solution list - we will want to merge that into our
+ * solutions list, replacing the original one in there (if it
+ * exists).
+ */
+
+ /* Take a copy of the original solutions boxes pointer. */
+ nxagentScreenBoxes *orig_solution_boxes = (*my_solution)->solution_boxes;
+
+ /* Copy work_screens to the solution boxes of my_solution. */
+ (*my_solution)->solution_boxes = nxagentScreenBoxesCopy(screen_boxes);
+
+ if (!((*my_solution)->solution_boxes)) {
+#ifdef WARNING
+ fprintf(stderr, "%s: unable to copy current screen state.\n", __func__);
+#endif
+
+ return(ret);
+ }
+
+ if ((!(orig_solution_boxes)) || (xorg_list_is_empty(&(orig_solution_boxes->head)))) {
+#ifdef WARNING
+ fprintf(stderr, "%s: original solution boxes list invalid or empty.\n", __func__);
+#endif
+
+ nxagentFreeScreenBoxes(orig_solution_boxes, TRUE);
+
+ SAFE_FREE(orig_solution_boxes);
+
+ return(ret);
+ }
+
+ /*
+ * Fetch actual screen box. The solutions list should only
+ * contain one element, so breaking out directly should be
+ * safe.
+ */
+ nxagentScreenBoxesElem *cur_box = xorg_list_first_entry(&(orig_solution_boxes->head), nxagentScreenBoxesElem, entry);
+
+ const Bool update = nxagentScreenBoxesUpdateScreenBox((*my_solution)->solution_boxes, screen_number, cur_box);
+
+ /*
+ * Outside of error handling, since we need to get rid of this
+ * data unconditionally.
+ */
+ nxagentFreeScreenBoxes(orig_solution_boxes, TRUE);
+
+ SAFE_FREE(orig_solution_boxes);
+
+ if (!(update)) {
+#ifdef WARNING
+ {
+ const unsigned long long screen_number_ = screen_number;
+ fprintf(stderr, "%s: unable to update solution screen number %llu.\n", __func__, screen_number_);
+ }
+#endif
+
+ return(ret);
+ }
+
+ /* Delete taken solution out of the list. */
+ xorg_list_del(&(first_entry->entry));
+
+ /* Get rid of the entry. */
+ nxagentScreenCrtcsFreeSolution(first_entry);
+
+ SAFE_FREE(first_entry);
+
+ ret = TRUE;
+
+ return(ret);
+}
+
+/*
+ * Declaration needed since the next function is using one that is only defined
+ * at a later point.
+ *
+ * Moving it up would be problematic since in that case we'd need even more
+ * declarations for other functions.
+ */
+static nxagentScreenCrtcsSolutions* nxagentScreenCrtcsGenerateSolutions(const nxagentScreenBoxes *all_boxes, const nxagentScreenBoxes *initial_screens, const size_t all_boxes_count, const size_t screen_count, const Bool *orig_screens_init);
+
+/*
+ * Helper handling all the solutions in best_screen_solutions recursively,
+ * adding them to the ret_solutions output parameter list.
+ *
+ * No pointer parameters may be NULL. screen_count and screen_number might be
+ * zero.
+ *
+ * Returns true on success, otherwise false.
+ */
+static Bool nxagentScreenCrtcsRecurseSolutions(const nxagentScreenBoxes *screen_boxes, const size_t screen_count, const size_t all_boxes_count, const Bool *screens_init, const size_t screen_number, const nxagentScreenCrtcsSolutions *best_screen_solutions, nxagentScreenCrtcsSolutions *ret_solutions) {
+ Bool ret = FALSE;
+
+ if ((!(screen_boxes)) || (!(screen_count)) || (!(all_boxes_count)) || (!(screens_init)) || (!(best_screen_solutions)) || (!(ret_solutions))) {
+ return(ret);
+ }
+
+ nxagentScreenCrtcsSolution *cur_solution = NULL;
+ xorg_list_for_each_entry(cur_solution, best_screen_solutions, entry) {
+ /* Other solutions will be handled recursively. */
+
+ /* Copy screens_init and set current screen value to true. */
+ Bool *recursive_screens_init = calloc(screen_count, sizeof(Bool));
+
+ if (!(recursive_screens_init)) {
+#ifdef WARNING
+ fprintf(stderr, "%s: unable to copy screen initialization array.\n", __func__);
+#endif
+
+ return(ret);
+ }
+
+ memmove(recursive_screens_init, screens_init, (screen_count * sizeof(Bool)));
+
+ recursive_screens_init[screen_number] = TRUE;
+
+ nxagentScreenBoxes *recursive_work_screens = nxagentScreenBoxesCopy(screen_boxes);
+
+ if (!(recursive_work_screens)) {
+#ifdef WARNING
+ fprintf(stderr, "%s: unable to copy current screen state.\n", __func__);
+#endif
+
+ SAFE_FREE(recursive_screens_init);
+
+ return(ret);
+ }
+
+ if ((!(cur_solution->solution_boxes)) || (xorg_list_is_empty(&(cur_solution->solution_boxes->head)))) {
+#ifdef WARNING
+ fprintf(stderr, "%s: current solution boxes list is empty or invalid. Algorithm error.\n", __func__);
+#endif
+
+ nxagentFreeScreenBoxes(recursive_work_screens, TRUE);
+
+ SAFE_FREE(recursive_work_screens);
+
+ SAFE_FREE(recursive_screens_init);
+
+ return(ret);
+ }
+
+ nxagentScreenBoxesElem *cur_box = xorg_list_first_entry(&(cur_solution->solution_boxes->head), nxagentScreenBoxesElem, entry);
+
+ const Bool update = nxagentScreenBoxesUpdateScreenBox(recursive_work_screens, screen_number, cur_box);
+
+ if (!(update)) {
+#ifdef WARNING
+ fprintf(stderr, "%s: unable to update screen state.\n", __func__);
+#endif
+
+ nxagentFreeScreenBoxes(recursive_work_screens, TRUE);
+
+ SAFE_FREE(recursive_screens_init);
+
+ SAFE_FREE(recursive_work_screens);
+
+ return(ret);
+ }
+
+ nxagentScreenCrtcsSolutions *tmp_solutions = nxagentScreenCrtcsGenerateSolutions(cur_solution->all_boxes, recursive_work_screens, all_boxes_count, screen_count, recursive_screens_init);
+
+ /* Get rid of the temporary screens init array again. */
+ SAFE_FREE(recursive_screens_init);
+
+ /* Get rid of the modified work screens list. */
+ nxagentFreeScreenBoxes(recursive_work_screens, TRUE);
+
+ SAFE_FREE(recursive_work_screens);
+
+ if (!(tmp_solutions)) {
+#ifdef WARNING
+ fprintf(stderr, "%s: unable to generate a new solutions list. Algorithm error.\n", __func__);
+#endif
+
+ return(ret);
+ }
+
+ /*
+ * tmp_solutions should now contain a list of possible solutions,
+ * add to ret_solutions.
+ */
+ nxagentScreenCrtcsSolution *cur_solution_it = NULL,
+ *next_solution = NULL;
+ xorg_list_for_each_entry_safe(cur_solution_it, next_solution, tmp_solutions, entry) {
+ xorg_list_del(&(cur_solution_it->entry));
+ xorg_list_append(&(cur_solution_it->entry), ret_solutions);
+ }
+
+ /* tmp_solutions should be empty now, safe to free. */
+ SAFE_FREE(tmp_solutions);
+ }
+
+ ret = TRUE;
+
+ return(ret);
+}
+
+/*
+ * Helper for handling solution lists. This probably is the heart of the screen
+ * extension code. The function is called once per extension run and calls
+ * other functions to save the very first solution and recursively generate
+ * alternative solutions.
+ *
+ * No pointer parameters might be NULL. max_rating is allowed to be any value,
+ * though it is implied that it is not the invalid value. init might be zero
+ * (i.e., false).
+ *
+ * Returns true on success, otherwise false.
+ */
+static Bool nxagentScreenCrtcsHandleSolutions(const nxagentScreenBoxes *all_boxes, const nxagentScreenBoxes *screen_boxes, const Bool init, const Bool *screens_init, const size_t screen_count, const size_t all_boxes_count, const double *screen_ratings, const double max_rating, nxagentScreenCrtcsSolutions *ret_solutions, ssize_t *screen_to_init, nxagentScreenCrtcsSolutions **best_screen_solutions, nxagentScreenCrtcsSolution **my_solution) {
+ Bool ret = FALSE;
+
+ if ((!(all_boxes)) || (!(screen_boxes)) || (!(screens_init)) || (!(screen_count)) || (!(all_boxes_count)) || (!(screen_ratings)) || (!(ret_solutions)) || (!(screen_to_init)) || (!(best_screen_solutions)) || (!(my_solution))) {
+ return(ret);
+ }
+
+ /*
+ * In case we have multiple solutions with a maximum rating, we need to
+ * consider each solution, which means branching off for all but one
+ * solution and only handling one solution in this run.
+ * Selecting the solution for the current run is tricky, though. We
+ * could either take the very first one, which is relatively easy, the
+ * last one, which is complicated because there might be multiple
+ * screens with a maximum rating and finding the last one is tricky
+ * with a spread-out array. Merging all solutions into one list and then
+ * taking the last element would be easy to do, but has the negative
+ * consequence of not being able to tell what screen the individual
+ * solutions belonged to originally - at least not without
+ * "sophisticated" means like keeping the original list and deep-checking
+ * objects for equality or creating another structure.
+ * Selecting a more or less random solution at the end of the first
+ * screen would work, but feels weird if there are more screens with
+ * potential solutions.
+ *
+ * Hence, let's go for selecting the very first solution.
+ */
+ Bool fetched_solution = FALSE;
+ (*screen_to_init) = -1;
+ for (size_t i = 0; i < screen_count; ++i) {
+ (*screen_to_init) = -1;
+
+ if (screen_ratings[i] == max_rating) {
+ /*
+ * This screen has a maximum rating.
+ * Its solution list may include more than one solution, though,
+ * which means that we have to branch off and consider each
+ * individual solution.
+ * At the very end, we select (potentially one of) the overall best
+ * solution.
+ */
+
+ if ((!(best_screen_solutions[i])) || (xorg_list_is_empty(best_screen_solutions[i]))) {
+#ifdef WARNING
+ fprintf(stderr, "%s: current screen marked with a maximum rating, but no solutions found in screen extension run. Algorithm error.\n", __func__);
+#endif
+
+ return(ret);
+ }
+
+ /*
+ * One or more solution(s), if necessary take the first one as the
+ * current solution and then branch off for the others.
+ */
+ if (!(fetched_solution)) {
+ Bool fetch = nxagentScreenCrtcsSelectSolution(screen_boxes, i, screens_init, best_screen_solutions[i], my_solution);
+
+ if (!(fetch)) {
+#ifdef WARNING
+ fprintf(stderr, "%s: error while selecting solution for current run.\n", __func__);
+#endif
+
+ return(ret);
+ }
+
+ fetched_solution = TRUE;
+
+ /*
+ * DO NOT modify other data (screen, all boxes or screen initialization
+ * array) here!
+ * We will need to change these variables eventually, but given
+ * that we may have further solutions to process/generate, doing
+ * it here would be an error.
+ * Refer to the later part of nxagentScreenCrtcsGenerateSolutions for
+ * this.
+ */
+ if (init) {
+ (*screen_to_init) = i;
+ }
+ }
+
+ Bool recursive_solutions = nxagentScreenCrtcsRecurseSolutions(screen_boxes, screen_count, all_boxes_count, screens_init, i, best_screen_solutions[i], ret_solutions);
+
+ if (!(recursive_solutions)) {
+#ifdef WARNING
+ fprintf(stderr, "%s: error while handling other solutions recursively in current run.\n", __func__);
+#endif
+
+ return(ret);
+ }
+ }
+ }
+
+ ret = TRUE;
+
+ return(ret);
+}
+
+/*
+ * Helper updating internal data in nxagentScreenCrtcsGenerateSolutions().
+ * This mostly exists to avoid complicated data freeing while updating the
+ * internal data.
+ *
+ * No pointers might be NULL. screens_to_init is allowed to be zero or
+ * negative, although negative values will not lead to changed data. This is
+ * not considered an error.
+ *
+ * Returns true on success, otherwise false.
+ */
+static Bool nxagentScreenCrtcsGenerateSolutionsUpdateInternalData(const nxagentScreenBoxes *all_boxes, const nxagentScreenBoxes *screen_boxes, const ssize_t screen_to_init, nxagentScreenBoxes **work_all_boxes, nxagentScreenBoxes **work_screens, Bool *screens_init) {
+ Bool ret = FALSE;
+
+ if ((!(all_boxes)) || (!(screen_boxes)) || (!(work_all_boxes)) || (!(work_screens)) || (!(screens_init))) {
+ return(ret);
+ }
+
+ nxagentFreeScreenBoxes((*work_all_boxes), TRUE);
+
+ SAFE_FREE((*work_all_boxes));
+
+ nxagentFreeScreenBoxes((*work_screens), TRUE);
+
+ SAFE_FREE((*work_screens));
+
+ (*work_all_boxes) = nxagentScreenBoxesCopy(all_boxes);
+
+ if (!((*work_all_boxes))) {
+#ifdef WARNING
+ fprintf(stderr, "%s: unable to copy current screen boxes.\n", __func__);
+#endif
+
+ return(ret);
+ }
+
+ (*work_screens) = nxagentScreenBoxesCopy(screen_boxes);
+
+ if (!((*work_screens))) {
+#ifdef WARNING
+ fprintf(stderr, "%s: unable to copy current screen boxes.\n", __func__);
+#endif
+
+ nxagentFreeScreenBoxes((*work_all_boxes), TRUE);
+
+ SAFE_FREE((*work_all_boxes));
+
+ return(ret);
+ }
+
+ /*
+ * Mark the current screen as initialized.
+ * DO NOT move this to the other functions, since we might have
+ * multiple screens with a maximum rating, but will not extend
+ * the other screens in the current run (but rather in recursive
+ * calls).
+ */
+ if (0 <= screen_to_init) {
+#ifdef WARNING
+ if (screens_init[screen_to_init]) {
+ const unsigned long long screen_number = screen_to_init;
+ fprintf(stderr, "%s: shall set screen init for screen number %llu to TRUE, but already marked as initialized. Algorithm warning.\n", __func__, screen_number);
+ }
+#endif
+
+ screens_init[screen_to_init] = TRUE;
+ }
+
+ ret = TRUE;
+
+ return(ret);
+}
+
+/*
+ * Helper generating a "fake" solution based on the passed-in data.
+ *
+ * This is useful if no screens needed extension.
+ *
+ * No pointer parameters might be NULL.
+ *
+ * On success, returns a "fake" solution, otherwise NULL.
+ */
+static nxagentScreenCrtcsSolution* nxagentScreenCrtcsGenerateFakeSolution(const nxagentScreenBoxes *all_boxes, const nxagentScreenBoxes *screen_boxes) {
+ nxagentScreenCrtcsSolution *ret = NULL;
+
+ if ((!(all_boxes)) || (!(screen_boxes))) {
+ return(ret);
+ }
+
+ ret = calloc(1, sizeof(nxagentScreenCrtcsSolution));
+
+ if (!(ret)) {
+ return(ret);
+ }
+
+ xorg_list_init(&(ret->entry));
+
+ ret->all_boxes = nxagentScreenBoxesCopy(all_boxes);
+
+ if (!(ret->all_boxes)) {
+ nxagentScreenCrtcsFreeSolution(ret);
+
+ SAFE_FREE(ret);
+
+ return(ret);
+ }
+
+ ret->solution_boxes = nxagentScreenBoxesCopy(screen_boxes);
+
+ if (!(ret->solution_boxes)) {
+ nxagentScreenCrtcsFreeSolution(ret);
+
+ SAFE_FREE(ret);
+
+ return(ret);
+ }
+
+ ret->rating_size_change = ret->rating_cover_penalty = ret->rating_extended_boxes_count = 0;
+ ret->rating = 0.0;
+
+ nxagentScreenCrtcsSolutionCalculateRating(ret, FALSE);
+
+ return(ret);
+}
+
+/*
+ * Helper generating a list of solutions, extending the initial screen boxes.
+ *
+ * All pointer arguments but orig_screens_init must be non-NULL. All size
+ * parameters must be non-zero.
+ *
+ * Returns either a pointer to the solutions list or NULL on failure.
+ */
+static nxagentScreenCrtcsSolutions* nxagentScreenCrtcsGenerateSolutions(const nxagentScreenBoxes *all_boxes, const nxagentScreenBoxes *initial_screens, const size_t all_boxes_count, const size_t screen_count, const Bool *orig_screens_init) {
+ nxagentScreenCrtcsSolutions *ret = NULL;
+
+ /*
+ * We assume that the screen and all boxes count as passed in match the
+ * actual data.
+ *
+ * We also assume that there is at least one screen. Otherwise, generating a
+ * fake one here and running an expensive algorithm on this which trivially
+ * will cover all base boxes anyway doesn't make a lot of sense.
+ * Theoretically, such a situation could occur if moving the nxagent window
+ * completely out of any screen bounds. This could potentially also happen if
+ * the window is initialized on a screen, which is later disconnected.
+ * Normally X11 window managers should take care of this situation and move
+ * the window to a connected screen again, but that doesn't happen on Windows
+ * for instance. This makes such windows inaccessible and would lead to an
+ * empty initial screens list.
+ */
+ if ((!(all_boxes)) || (!(initial_screens)) || (!(all_boxes_count)) || (!(screen_count))) {
+ return(ret);
+ }
+
+ /* Check that initial_screens and all_boxes are not empty. */
+ /* FIXME: xorg_list_is_empty is not const-correct. */
+ if ((xorg_list_is_empty((struct xorg_list *)(&(initial_screens->head)))) || (xorg_list_is_empty((struct xorg_list *)(&(all_boxes->head))))) {
+#ifdef WARNING
+ fprintf(stderr, "%s: initial_screens or all_boxes empty, assuming error and returning NULL.\n", __func__);
+#endif
+
+ return(ret);
+ }
+
+ Bool err = FALSE;
+ size_t obsolete_boxes_count = nxagentScreenBoxesObsoleteCount(all_boxes, &err);
+
+ if (err) {
+ return(ret);
+ }
+
+#ifdef DEBUG
+ {
+ const unsigned long long obsolete_boxes_count_ = obsolete_boxes_count;
+ fprintf(stderr, "%s: calculated initial obsolete boxes count: %llu\n", __func__, obsolete_boxes_count_);
+ }
+#endif
+
+ /*
+ * orig_screens_init as passed-in to the function (if non-NULL) will serve as
+ * the base initialization of the array.
+ * Each function execution is reponsible for freeing the memory at the end -
+ * not callees.
+ */
+ Bool *screens_init = calloc(screen_count, sizeof(Bool));
+
+ if (!(screens_init)) {
+ return(ret);
+ }
+
+ if (orig_screens_init) {
+ memmove(screens_init, orig_screens_init, (screen_count * sizeof(*screens_init)));
+ }
+
+ /*
+ * Let work_screens and work_all_boxes point to initial_screens and all_boxes
+ * respectively.
+ */
+ nxagentScreenBoxes *work_screens = nxagentScreenBoxesCopy(initial_screens);
+
+ if (!(work_screens)) {
+ SAFE_FREE(screens_init);
+
+ return(ret);
+ }
+
+ nxagentScreenBoxes *work_all_boxes = nxagentScreenBoxesCopy(all_boxes);
+
+ if (!(work_all_boxes)) {
+ nxagentFreeScreenBoxes(work_screens, TRUE);
+
+ SAFE_FREE(work_screens);
+
+ SAFE_FREE(screens_init);
+
+ return(ret);
+ }
+
+ ret = calloc(1, sizeof(nxagentScreenCrtcsSolutions));
+
+ if (!(ret)) {
+ nxagentFreeScreenBoxes(work_screens, TRUE);
+ nxagentFreeScreenBoxes(work_all_boxes, TRUE);
+
+ SAFE_FREE(work_screens);
+ SAFE_FREE(work_all_boxes);
+
+ SAFE_FREE(screens_init);
+
+ return(ret);
+ }
+
+ xorg_list_init(ret);
+
+ Bool init = TRUE;
+ nxagentScreenCrtcsSolution *my_solution = NULL;
+ while (obsolete_boxes_count < all_boxes_count) {
+ ssize_t screen_to_init = -1;
+
+ nxagentScreenCrtcsSolutions **extended_screens = nxagentScreenCrtcsGeneratePotentialSolutionArray(work_all_boxes, work_screens, screen_count);
+
+ if (!(extended_screens)) {
+ nxagentScreenCrtcsFreeSolutions(ret);
+
+ nxagentScreenCrtcsFreeSolution(my_solution);
+
+ nxagentFreeScreenBoxes(work_screens, TRUE);
+ nxagentFreeScreenBoxes(work_all_boxes, TRUE);
+
+ SAFE_FREE(ret);
+
+ SAFE_FREE(my_solution);
+
+ SAFE_FREE(work_screens);
+ SAFE_FREE(work_all_boxes);
+
+ SAFE_FREE(screens_init);
+
+ return(ret);
+ }
+
+ init = FALSE;
+
+ /* If one screen wasn't extended yet, init should be true. Sync state. */
+ for (size_t i = 0; i < screen_count; ++i) {
+ init |= (!(screens_init[i]));
+ }
+
+ nxagentScreenCrtcsSolutions **best_screen_solutions = NULL;
+ double *screen_ratings = NULL,
+ max_rating = 0;
+
+ /*
+ * Could work without an explicit cast, but C doesn't implement a more
+ * complicated implicit cast rule while C++ does.
+ */
+ Bool filter = nxagentScreenCrtcsFilterScreenSolutions((const nxagentScreenCrtcsSolutions * const *)(extended_screens), screen_count, init, screens_init, &best_screen_solutions, &screen_ratings, &max_rating);
+
+ /*
+ * Clean up extended_screens. We don't need it any longer.
+ * Do this before error handling, since it will need to be free'd in any
+ * case.
+ */
+ for (size_t i = 0; i < screen_count; ++i) {
+ nxagentScreenCrtcsFreeSolutions(extended_screens[i]);
+
+ SAFE_FREE(extended_screens[i]);
+ }
+
+ SAFE_FREE(extended_screens);
+
+ if (!(filter)) {
+ for (size_t i = 0; i < screen_count; ++i) {
+ nxagentScreenCrtcsFreeSolutions(best_screen_solutions[i]);
+
+ SAFE_FREE(best_screen_solutions[i]);
+ }
+
+ nxagentScreenCrtcsFreeSolutions(ret);
+
+ nxagentScreenCrtcsFreeSolution(my_solution);
+
+ nxagentFreeScreenBoxes(work_screens, TRUE);
+ nxagentFreeScreenBoxes(work_all_boxes, TRUE);
+
+ SAFE_FREE(best_screen_solutions);
+
+ SAFE_FREE(ret);
+
+ SAFE_FREE(my_solution);
+
+ SAFE_FREE(work_screens);
+ SAFE_FREE(work_all_boxes);
+
+ SAFE_FREE(screens_init);
+ SAFE_FREE(screen_ratings);
+
+ return(ret);
+ }
+
+ Bool solution_handling = nxagentScreenCrtcsHandleSolutions(work_all_boxes, work_screens, init, screens_init, screen_count, all_boxes_count, screen_ratings, max_rating, ret, &screen_to_init, best_screen_solutions, &my_solution);
+
+ /* Unconditionally get rid of best_screen_solutions. */
+ for (size_t i = 0; i < screen_count; ++i) {
+ nxagentScreenCrtcsFreeSolutions(best_screen_solutions[i]);
+
+ SAFE_FREE(best_screen_solutions[i]);
+ }
+
+ SAFE_FREE(best_screen_solutions);
+
+ /* And screen_ratings. */
+ SAFE_FREE(screen_ratings);
+
+ if (!(solution_handling)) {
+#ifdef WARNING
+ fprintf(stderr, "%s: unable to handle screen boxes in current run.\n", __func__);
+#endif
+
+ nxagentScreenCrtcsFreeSolutions(ret);
+
+ nxagentScreenCrtcsFreeSolution(my_solution);
+
+ nxagentFreeScreenBoxes(work_screens, TRUE);
+ nxagentFreeScreenBoxes(work_all_boxes, TRUE);
+
+ SAFE_FREE(ret);
+
+ SAFE_FREE(my_solution);
+
+ SAFE_FREE(work_screens);
+ SAFE_FREE(work_all_boxes);
+
+ SAFE_FREE(screens_init);
+
+ return(ret);
+ }
+
+ /*
+ * This is actually the right place to change these variables. For more
+ * information, refer to comments in the other functions.
+ */
+ Bool update_data = nxagentScreenCrtcsGenerateSolutionsUpdateInternalData(my_solution->all_boxes, my_solution->solution_boxes, screen_to_init, &work_all_boxes, &work_screens, screens_init);
+
+ if (!(update_data)) {
+#ifdef WARNING
+ fprintf(stderr, "%s: unable to update internal data in current run.\n", __func__);
+#endif
+
+ nxagentScreenCrtcsFreeSolutions(ret);
+
+ nxagentScreenCrtcsFreeSolution(my_solution);
+
+ nxagentFreeScreenBoxes(work_screens, TRUE);
+ nxagentFreeScreenBoxes(work_all_boxes, TRUE);
+
+ SAFE_FREE(ret);
+
+ SAFE_FREE(my_solution);
+
+ SAFE_FREE(work_screens);
+ SAFE_FREE(work_all_boxes);
+
+ SAFE_FREE(screens_init);
+
+ return(ret);
+ }
+
+ obsolete_boxes_count = nxagentScreenBoxesObsoleteCount(work_all_boxes, &err);
+
+ if (err) {
+#ifdef WARNING
+ fprintf(stderr, "%s: unable to update obsolete base boxes.\n", __func__);
+#endif
+
+ nxagentScreenCrtcsFreeSolutions(ret);
+
+ nxagentScreenCrtcsFreeSolution(my_solution);
+
+ nxagentFreeScreenBoxes(work_screens, TRUE);
+ nxagentFreeScreenBoxes(work_all_boxes, TRUE);
+
+ SAFE_FREE(ret);
+
+ SAFE_FREE(my_solution);
+
+ SAFE_FREE(work_screens);
+ SAFE_FREE(work_all_boxes);
+
+ SAFE_FREE(screens_init);
+
+ return(ret);
+ }
+
+#ifdef DEBUG
+ {
+ const unsigned long long obsolete_boxes_count_ = obsolete_boxes_count;
+ fprintf(stderr, "%s: recalculated obsolete boxes count: %llu\n", __func__, obsolete_boxes_count_);
+ }
+#endif
+ }
+
+ /* Unconditional cleanup. */
+ SAFE_FREE(screens_init);
+
+ /*
+ * Having no solution means that we didn't have to generate one, i.e., that
+ * the original screen boxes were all extended in the first place.
+ *
+ * In such a case, copy the input data and recalculate the rating with size
+ * changes set to zero.
+ */
+ if (!(my_solution)) {
+ my_solution = nxagentScreenCrtcsGenerateFakeSolution(work_all_boxes, work_screens);
+ }
+
+ /* Cleanup. */
+ nxagentFreeScreenBoxes(work_screens, TRUE);
+ nxagentFreeScreenBoxes(work_all_boxes, TRUE);
+
+ SAFE_FREE(work_screens);
+ SAFE_FREE(work_all_boxes);
+
+ if (!(my_solution)) {
+#ifdef WARNING
+ fprintf(stderr, "%s: unable to generate \"fake\" solution.\n", __func__);
+#endif
+
+ nxagentScreenCrtcsFreeSolutions(ret);
+
+ SAFE_FREE(ret);
+
+ return(ret);
+ }
+
+ /*
+ * Reaching this point means that we've extended everything to cover all
+ * non-obsoleted base boxes.
+ *
+ * my_solution isn't part of ret yet, so add it.
+ */
+ xorg_list_append(&(my_solution->entry), ret);
+
+ /*
+ * At the end of this function, we should only have fully extended solutions
+ * (i.e., no partial ones).
+ * Due to that, extracing the best solution(s) should work fine and leave out
+ * solutions that are not interesting to us.
+ */
+ nxagentScreenCrtcsSolutions *best_ret = nxagentScreenCrtcsExtractBestSolutions(ret);
+
+ /* Get rid of old solutions list. */
+ nxagentScreenCrtcsFreeSolutions(ret);
+
+ SAFE_FREE(ret);
+
+ ret = best_ret;
+
+
+ return(ret);
+}
+
+/*
Destroy an output after removing it from any crtc that might reference it
*/
void nxagentDropOutput(RROutputPtr o) {
--
Alioth's /home/x2go-admin/maintenancescripts/git/hooks/post-receive-email on /srv/git/code.x2go.org/nx-libs.git
More information about the x2go-commits
mailing list