Skip to content

Commit

Permalink
nx-X11/programs/Xserver/hw/nxagent/Screen.c: add low-level screen box…
Browse files Browse the repository at this point in the history
…es merging algorithm/functions.
  • Loading branch information
Ionic committed Mar 23, 2018
1 parent 64a9ba5 commit 0917bfc
Showing 1 changed file with 373 additions and 0 deletions.
373 changes: 373 additions & 0 deletions nx-X11/programs/Xserver/hw/nxagent/Screen.c
Original file line number Diff line number Diff line change
Expand Up @@ -4354,6 +4354,379 @@ static nxagentScreenBoxes* nxagentGenerateScreenCrtcs(nxagentScreenSplits *split
return(ret);
}

/*
* Helper returning true of a given box intersects with a given screen,
* otherwise and on error false.
*/
static Bool nxagentScreenBoxIntersectsScreen(const nxagentScreenBoxesElem *box, const XineramaScreenInfo *screen_info) {
Bool ret = FALSE;

if ((!(box)) || (!(box->box)) || (!(screen_info))) {
return(ret);
}

/* Find out if this box has intersections with display. */
INT32 box_width = (box->box->x2 - box->box->x1),
box_height = (box->box->y2 - box->box->y1);
ret = intersect(box->box->x1, box->box->y1,
box_width, box_height,
screen_info->x_org, screen_info->y_org,
screen_info->width, screen_info->height,
NULL, NULL, NULL, NULL);

return(ret);
}

/* Helper printing out a single screen box. */
static char* nxagentPrintScreenBoxElem(const nxagentScreenBoxesElem *box) {
char *ret = NULL;

BoxPtr box_data = box->box;
if ((!(box)) || (!(box_data))) {
return(ret);
}

char *construct = NULL;
if (-1 == asprintf(&construct, "[(%d, %d), (%d, %d)]", box_data->x1, box_data->y1, box_data->x2, box_data->y2)) {
return(ret);
}

ret = construct;

return(ret);
}

/*
* Helper comparing two box elements.
* Returns true if both data sections match, false otherwise or on error.
*/
static Bool nxagentCompareScreenBoxData(const BoxPtr lhs, const BoxPtr rhs) {
Bool ret = FALSE;

if ((!(lhs)) || (!(rhs))) {
return(ret);
}

if ((lhs->x1 == rhs->x1) && (lhs->x2 == rhs->x2) && (lhs->y1 == rhs->y1) && (lhs->y2 == rhs->y2)) {
ret = TRUE;
}

return(ret);
}

/*
* Helper merging boxes on a low level.
* Returns true if merging succeeded, otherwise or on error false.
*
* If merging succeded, the merge_boxes list shall contain only one element:
* the extended box representing a screen.
*
* In case of errors, the original list may or may not be modified.
* Assume that the data is invalid.
*/
static Bool nxagentMergeBoxes(nxagentScreenBoxes *all_boxes, nxagentScreenBoxes *merge_boxes) {
Bool ret = FALSE;

if (!(all_boxes)) || (!(merge_boxes)) {
return(ret);
}

/* Naïve approach: merge of all boxes is the bounding box. */
BoxRec bounding_box = { 0 };
nxagentScreenBoxesElem *cur = NULL;
Bool init = FALSE;
size_t merge_boxes_count = 0;
xorg_list_for_each_entry(cur, merge_boxes, entry) {
if (!(cur->box)) {
return(ret);
}

if (!(init)) {
bounding_box.x1 = cur->box->x1;
bounding_box.x2 = cur->box->x2;
bounding_box.y1 = cur->box->y1;
bounding_box.y2 = cur->box->y2;

init = TRUE;

++merge_boxes_count;

continue;
}

bounding_box.x1 = MIN(cur->box->x1, bounding_box.x1);
bounding_box.x2 = MAX(cur->box->x2, bounding_box.x2);
bounding_box.y1 = MIN(cur->box->y1, bounding_box.y1);
bounding_box.y2 = MAX(cur->box->y2, bounding_box.y2);

++merge_boxes_count;
}

if (1 >= merge_boxes_count) {
/* Special case: empty or one-element merge list. */
ret = TRUE;
return(ret);
}

/* Try to find a suitable merge pair. */
cur = NULL;
nxagentScreenBoxesElem *merge_rhs = NULL;
Bool restart = TRUE;
while (restart) {
restart = FALSE;

xorg_list_for_each_entry(cur, merge_boxes, entry) {
if (!(cur->box)) {
return(ret);
}

xorg_list_for_each_entry(merge_rhs, &(cur->entry), entry) {
if (!(merge_rhs->box)) {
return(ret);
}

if (&(merge_rhs->entry) == merge_boxes) {
/* Reached end of list. */
merge_rhs = NULL;
break;
}

/* Check adjacency. */
/* left side right side */
if ((cur->box->x1 == merge_rhs->box->x2) || (cur->box->x2 == merge_rhs->box->x1)) {
/* Check starting y position and height. */
if ((cur->box->y1 == merge_rhs->box->y1) && (cur->box->y2 == merge_rhs->box->y2)) {
break;
}
}

/* top side bottom side */
if ((cur->box->y1 == merge_rhs->box->y2) || (cur->box->y2 == merge_rhs->box->y1)) {
/* Check starting x position and width. */
if ((cur->box->x1 == merge_rhs->box->x1) && (cur->box->x2 == merge_rhs->box->x2)) {
break;
}
}
}

/* cur and merge_rhs are mergeable. */
if (merge_rhs) {
#ifdef DEBUG
{
char *box_left_str = nxagentPrintScreenBoxElem(cur);
char *box_right_str = nxagentPrintScreenBoxElem(merge_rhs);

fprintf(stderr, "%s: mergeable boxes found: ", __func__);
if (!(box_left_str)) {
fprintf(stderr, "box with invalid data [%p]\n", cur);
}
else {
fprintf(stderr, "%s\n", box_left_str);
}

if (!(box_right_str)) {
fprintf(stderr, ", box with invalid data [%p]\n", merge_rhs);
}
else {
fprintf(stderr, ", %s\n", box_right_str);
}

SAFE_FREE(box_left_str);
SAFE_FREE(box_right_str);
}
#endif
cur->box->x1 = MIN(cur->box->x1, merge_rhs->box->x1);
cur->box->x2 = MAX(cur->box->x2, merge_rhs->box->x2);
cur->box->y1 = MIN(cur->box->y1, merge_rhs->box->y1);
cur->box->y2 = MAX(cur->box->y2, merge_rhs->box->y2);

/* Delete merge_rhs box out of merge list ... */
xorg_list_del(&(merge_rhs->entry));

/*
* ... and mark an equivalent box in the all boxes list as obsolete.
*
* Note that it is not an error condition if no such box exists in the
* all boxes list. More likely we tried to mark a box obsolete that
* has already been merged with a different one (and now covers more
* than one entry in the all boxes list).
*/
if (merge_rhs->obsolete) {
nxagentScreenBoxesElem *cur_obsolete = NULL;
xorg_list_for_each_entry(cur_obsolete, all_boxes, entry) {
if (nxagentCompareScreenBoxData(cur_obsolete->box, merge_rhs->box)) {
cur_obsolete->obsolete = TRUE;

/*
* Set iterator to NULL and break out, this condition marks that
* a box has been deleted out of the bigger array.
*/
cur_obsolete = NULL;

break;
}
}

if (cur_obsolete) {
/* Non-NULL means that we haven't found a box to mark obsolete. */
#ifdef WARNING
fprintf(stderr, "%s: merged boxes from merge list, but couldn't find right-hand box in original list - box has likely already been merged into a different one.\n", __func__);
#endif
}
}

/*
* Remove merge_rhs's internal box data.
* Since it's a deep copy, only this element will be affected.
*/
SAFE_FREE(merge_rhs->box);

/*
* At this point, merge_rhs's data has been free()d and the box
* element is not part of the merge_boxes lists.
* Delete the box element.
*/
SAFE_FREE(merge_rhs);

restart = TRUE;

break;
}
else {
#ifdef DEBUG
char *box_str = nxagentPrintScreenBoxElem(cur);

fprintf(stderr, "%s: no mergeable box found for ", __func__);
if (box_str) {
fprintf(stderr, " current box %s\n", box_str);
}
else {
fprintf(stderr, " box with invalid data [%p]\n", cur);
}

SAFE_FREE(box_str);
#endif
}
}
}

/* All boxes merged, we should only have one left. */
merge_boxes_count = 0;
xorg_list_for_each_entry(cur, merge_boxes, entry) {
if (!(cur->box)) {
return(ret);
}

++merge_boxes_count;
}

if (1 < merge_boxes_count) {
#ifdef WARNING
fprintf(stderr, "%s: WARNING: box merge operation produced more than one box - initial merge list was not a rectangle!\n", __func__);
#endif
}
else if (0 == merge_boxes_count) {
#ifdef WARNING
fprintf(stderr, "%s: WARNING: box merge operation produced a merged box count of 0!\n", __func__);
#endif
}
else {
cur = NULL;
xorg_list_for_each_entry(cur, merge_boxes, entry) {
/* Just break out here directly, there should only be one box. */
break;
}

if (nxagentCompareScreenBoxData(&bounding_box, cur->box)) {
#ifdef DEBUG
fprintf("%s: merging operations result is equal to bounding box, could have avoided complex calculations.\n", __func__);
#endif
ret = TRUE;
}
}

return(ret);
}

/* Helper merging boxes that pertain to specific screen.
*
* In case of errors, the original list may or may not be modified.
* Assume that the data is invalid.
*/
static Bool nxagentMergeScreenBoxes(nxagentScreenBoxes *all_boxes, nxagentScreenBoxes *screen_boxes, const XineramaScreenInfo *screen_info, const size_t screen_count) {
Bool ret = FALSE;

if ((!(all_boxes)) || (!(screen_boxes)) || (!(screen_info)) || (!(screen_count))) {
return(ret);
}

for (size_t i = 0; i < screen_count; ++i) {
/*
* Structure holding the box elements intersecting with
* the current screen.
*/
nxagentScreenBoxes *cur_screen_boxes = screen_boxes[i];
xorg_list_init(&cur_screen_boxes);

nxagentScreenBoxesElem *cur_box = NULL;
xorg_list_for_each_entry(cur_box, all_boxes, entry) {
Bool cur_intersect = nxagentScreenBoxIntersectsScreen(cur_box, &(screen_info[i]));

if (cur_intersect) {
/*
* If a screen intersects the current box, we must:
* - create a deep copy of this box
* - add the copy to the screen boxes list
* - set the obsolete flag appropriately
* - start the low-level merge operation on the screen boxes list
*
* After this, assuming no error happened, the screen boxes list will
* contain only one element: a box containing the screen area.
*/
nxagentScreenBoxesElem *box_copy = calloc(sizeof(nxagentScreenBoxesElem), 1);

if (!(box_copy)) {
nxagentFreeScreenBoxes(all_boxes, TRUE);
return(ret);
}

/* Create new low-level BoxRec. */
box_copy->box = calloc(sizeof(BoxRec), 1);

if (!(box_copy->box)) {
nxagentFreeScreenBoxes(cur_box, TRUE);
nxagentFreeScreenBoxes(all_boxes, TRUE);
return(ret);
}

/* Copy BoxRec contents. */
memmove(box_copy->box, cur_box->box, sizeof(BoxRec));

box_copy->obsolete = TRUE;

for (size_t y = (i + 1); y < screen_count; ++y) {
if (nxagentScreenBoxIntersectsScreen(cur_box, &(screen_info[y]))) {
/* Protect box, if still needed later on after merging this set. */
box_copy->obsolete = FALSE;
break;
}
}

xorg_list_append(&(box_copy->entry), cur_screen_boxes);
}
}

/* Actually merge the boxes. */
if (!(nxagentMergeBoxes(all_boxes, cur_screen_boxes))) {
return(ret);
}
}

ret = TRUE;

return(ret);
}

/*
Destroy an output after removing it from any crtc that might reference it
*/
Expand Down

0 comments on commit 0917bfc

Please sign in to comment.