Skip to content

Commit d3d3b8e

Browse files
committed
nx-X11/programs/Xserver/hw/nxagent/Screen.c: add low-level screen boxes merging algorithm/functions.
1 parent 64a9ba5 commit d3d3b8e

File tree

1 file changed

+373
-0
lines changed
  • nx-X11/programs/Xserver/hw/nxagent

1 file changed

+373
-0
lines changed

nx-X11/programs/Xserver/hw/nxagent/Screen.c

+373
Original file line numberDiff line numberDiff line change
@@ -4354,6 +4354,379 @@ static nxagentScreenBoxes* nxagentGenerateScreenCrtcs(nxagentScreenSplits *split
43544354
return(ret);
43554355
}
43564356

4357+
/*
4358+
* Helper returning true of a given box intersects with a given screen,
4359+
* otherwise and on error false.
4360+
*/
4361+
static Bool nxagentScreenBoxIntersectsScreen(const nxagentScreenBoxesElem *box, const XineramaScreenInfo *screen_info) {
4362+
Bool ret = FALSE;
4363+
4364+
if ((!(box)) || (!(box->box)) || (!(screen_info))) {
4365+
return(ret);
4366+
}
4367+
4368+
/* Find out if this box has intersections with display. */
4369+
INT32 box_width = (box->box->x2 - box->box->x1),
4370+
box_height = (box->box->y2 - box->box->y1);
4371+
ret = intersect(box->box->x1, box->box->y1,
4372+
box_width, box_height,
4373+
screen_info->x_org, screen_info->y_org,
4374+
screen_info->width, screen_info->height,
4375+
NULL, NULL, NULL, NULL);
4376+
4377+
return(ret);
4378+
}
4379+
4380+
/* Helper printing out a single screen box. */
4381+
static char* nxagentPrintScreenBoxElem(const nxagentScreenBoxesElem *box) {
4382+
char *ret = NULL;
4383+
4384+
BoxPtr box_data = box->box;
4385+
if ((!(box)) || (!(box_data))) {
4386+
return(ret);
4387+
}
4388+
4389+
char *construct = NULL;
4390+
if (-1 == asprintf(&construct, "[(%d, %d), (%d, %d)]", box_data->x1, box_data->y1, box_data->x2, box_data->y2)) {
4391+
return(ret);
4392+
}
4393+
4394+
ret = construct;
4395+
4396+
return(ret);
4397+
}
4398+
4399+
/*
4400+
* Helper comparing two box elements.
4401+
* Returns true if both data sections match, false otherwise or on error.
4402+
*/
4403+
static Bool nxagentCompareScreenBoxData(const BoxPtr lhs, const BoxPtr rhs) {
4404+
Bool ret = FALSE;
4405+
4406+
if ((!(lhs)) || (!(rhs))) {
4407+
return(ret);
4408+
}
4409+
4410+
if ((lhs->x1 == rhs->x1) && (lhs->x2 == rhs->x2) && (lhs->y1 == rhs->y1) && (lhs->y2 == rhs->y2)) {
4411+
ret = TRUE;
4412+
}
4413+
4414+
return(ret);
4415+
}
4416+
4417+
/*
4418+
* Helper merging boxes on a low level.
4419+
* Returns true if merging succeeded, otherwise or on error false.
4420+
*
4421+
* If merging succeded, the merge_boxes list shall contain only one element:
4422+
* the extended box representing a screen.
4423+
*
4424+
* In case of errors, the original list may or may not be modified.
4425+
* Assume that the data is invalid.
4426+
*/
4427+
static Bool nxagentMergeBoxes(nxagentScreenBoxes *all_boxes, nxagentScreenBoxes *merge_boxes) {
4428+
Bool ret = FALSE;
4429+
4430+
if ((!(all_boxes)) || (!(merge_boxes))) {
4431+
return(ret);
4432+
}
4433+
4434+
/* Naïve approach: merge of all boxes is the bounding box. */
4435+
BoxRec bounding_box = { 0 };
4436+
nxagentScreenBoxesElem *cur = NULL;
4437+
Bool init = FALSE;
4438+
size_t merge_boxes_count = 0;
4439+
xorg_list_for_each_entry(cur, merge_boxes, entry) {
4440+
if (!(cur->box)) {
4441+
return(ret);
4442+
}
4443+
4444+
if (!(init)) {
4445+
bounding_box.x1 = cur->box->x1;
4446+
bounding_box.x2 = cur->box->x2;
4447+
bounding_box.y1 = cur->box->y1;
4448+
bounding_box.y2 = cur->box->y2;
4449+
4450+
init = TRUE;
4451+
4452+
++merge_boxes_count;
4453+
4454+
continue;
4455+
}
4456+
4457+
bounding_box.x1 = MIN(cur->box->x1, bounding_box.x1);
4458+
bounding_box.x2 = MAX(cur->box->x2, bounding_box.x2);
4459+
bounding_box.y1 = MIN(cur->box->y1, bounding_box.y1);
4460+
bounding_box.y2 = MAX(cur->box->y2, bounding_box.y2);
4461+
4462+
++merge_boxes_count;
4463+
}
4464+
4465+
if (1 >= merge_boxes_count) {
4466+
/* Special case: empty or one-element merge list. */
4467+
ret = TRUE;
4468+
return(ret);
4469+
}
4470+
4471+
/* Try to find a suitable merge pair. */
4472+
cur = NULL;
4473+
nxagentScreenBoxesElem *merge_rhs = NULL;
4474+
Bool restart = TRUE;
4475+
while (restart) {
4476+
restart = FALSE;
4477+
4478+
xorg_list_for_each_entry(cur, merge_boxes, entry) {
4479+
if (!(cur->box)) {
4480+
return(ret);
4481+
}
4482+
4483+
xorg_list_for_each_entry(merge_rhs, &(cur->entry), entry) {
4484+
if (!(merge_rhs->box)) {
4485+
return(ret);
4486+
}
4487+
4488+
if (&(merge_rhs->entry) == merge_boxes) {
4489+
/* Reached end of list. */
4490+
merge_rhs = NULL;
4491+
break;
4492+
}
4493+
4494+
/* Check adjacency. */
4495+
/* left side right side */
4496+
if ((cur->box->x1 == merge_rhs->box->x2) || (cur->box->x2 == merge_rhs->box->x1)) {
4497+
/* Check starting y position and height. */
4498+
if ((cur->box->y1 == merge_rhs->box->y1) && (cur->box->y2 == merge_rhs->box->y2)) {
4499+
break;
4500+
}
4501+
}
4502+
4503+
/* top side bottom side */
4504+
if ((cur->box->y1 == merge_rhs->box->y2) || (cur->box->y2 == merge_rhs->box->y1)) {
4505+
/* Check starting x position and width. */
4506+
if ((cur->box->x1 == merge_rhs->box->x1) && (cur->box->x2 == merge_rhs->box->x2)) {
4507+
break;
4508+
}
4509+
}
4510+
}
4511+
4512+
/* cur and merge_rhs are mergeable. */
4513+
if (merge_rhs) {
4514+
#ifdef DEBUG
4515+
{
4516+
char *box_left_str = nxagentPrintScreenBoxElem(cur);
4517+
char *box_right_str = nxagentPrintScreenBoxElem(merge_rhs);
4518+
4519+
fprintf(stderr, "%s: mergeable boxes found: ", __func__);
4520+
if (!(box_left_str)) {
4521+
fprintf(stderr, "box with invalid data [%p]\n", cur);
4522+
}
4523+
else {
4524+
fprintf(stderr, "%s\n", box_left_str);
4525+
}
4526+
4527+
if (!(box_right_str)) {
4528+
fprintf(stderr, ", box with invalid data [%p]\n", merge_rhs);
4529+
}
4530+
else {
4531+
fprintf(stderr, ", %s\n", box_right_str);
4532+
}
4533+
4534+
SAFE_FREE(box_left_str);
4535+
SAFE_FREE(box_right_str);
4536+
}
4537+
#endif
4538+
cur->box->x1 = MIN(cur->box->x1, merge_rhs->box->x1);
4539+
cur->box->x2 = MAX(cur->box->x2, merge_rhs->box->x2);
4540+
cur->box->y1 = MIN(cur->box->y1, merge_rhs->box->y1);
4541+
cur->box->y2 = MAX(cur->box->y2, merge_rhs->box->y2);
4542+
4543+
/* Delete merge_rhs box out of merge list ... */
4544+
xorg_list_del(&(merge_rhs->entry));
4545+
4546+
/*
4547+
* ... and mark an equivalent box in the all boxes list as obsolete.
4548+
*
4549+
* Note that it is not an error condition if no such box exists in the
4550+
* all boxes list. More likely we tried to mark a box obsolete that
4551+
* has already been merged with a different one (and now covers more
4552+
* than one entry in the all boxes list).
4553+
*/
4554+
if (merge_rhs->obsolete) {
4555+
nxagentScreenBoxesElem *cur_obsolete = NULL;
4556+
xorg_list_for_each_entry(cur_obsolete, all_boxes, entry) {
4557+
if (nxagentCompareScreenBoxData(cur_obsolete->box, merge_rhs->box)) {
4558+
cur_obsolete->obsolete = TRUE;
4559+
4560+
/*
4561+
* Set iterator to NULL and break out, this condition marks that
4562+
* a box has been deleted out of the bigger array.
4563+
*/
4564+
cur_obsolete = NULL;
4565+
4566+
break;
4567+
}
4568+
}
4569+
4570+
if (cur_obsolete) {
4571+
/* Non-NULL means that we haven't found a box to mark obsolete. */
4572+
#ifdef WARNING
4573+
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__);
4574+
#endif
4575+
}
4576+
}
4577+
4578+
/*
4579+
* Remove merge_rhs's internal box data.
4580+
* Since it's a deep copy, only this element will be affected.
4581+
*/
4582+
SAFE_FREE(merge_rhs->box);
4583+
4584+
/*
4585+
* At this point, merge_rhs's data has been free()d and the box
4586+
* element is not part of the merge_boxes lists.
4587+
* Delete the box element.
4588+
*/
4589+
SAFE_FREE(merge_rhs);
4590+
4591+
restart = TRUE;
4592+
4593+
break;
4594+
}
4595+
else {
4596+
#ifdef DEBUG
4597+
char *box_str = nxagentPrintScreenBoxElem(cur);
4598+
4599+
fprintf(stderr, "%s: no mergeable box found for ", __func__);
4600+
if (box_str) {
4601+
fprintf(stderr, " current box %s\n", box_str);
4602+
}
4603+
else {
4604+
fprintf(stderr, " box with invalid data [%p]\n", cur);
4605+
}
4606+
4607+
SAFE_FREE(box_str);
4608+
#endif
4609+
}
4610+
}
4611+
}
4612+
4613+
/* All boxes merged, we should only have one left. */
4614+
merge_boxes_count = 0;
4615+
xorg_list_for_each_entry(cur, merge_boxes, entry) {
4616+
if (!(cur->box)) {
4617+
return(ret);
4618+
}
4619+
4620+
++merge_boxes_count;
4621+
}
4622+
4623+
if (1 < merge_boxes_count) {
4624+
#ifdef WARNING
4625+
fprintf(stderr, "%s: WARNING: box merge operation produced more than one box - initial merge list was not a rectangle!\n", __func__);
4626+
#endif
4627+
}
4628+
else if (0 == merge_boxes_count) {
4629+
#ifdef WARNING
4630+
fprintf(stderr, "%s: WARNING: box merge operation produced a merged box count of 0!\n", __func__);
4631+
#endif
4632+
}
4633+
else {
4634+
cur = NULL;
4635+
xorg_list_for_each_entry(cur, merge_boxes, entry) {
4636+
/* Just break out here directly, there should only be one box. */
4637+
break;
4638+
}
4639+
4640+
if (nxagentCompareScreenBoxData(&bounding_box, cur->box)) {
4641+
#ifdef DEBUG
4642+
fprintf("%s: merging operations result is equal to bounding box, could have avoided complex calculations.\n", __func__);
4643+
#endif
4644+
ret = TRUE;
4645+
}
4646+
}
4647+
4648+
return(ret);
4649+
}
4650+
4651+
/* Helper merging boxes that pertain to specific screen.
4652+
*
4653+
* In case of errors, the original list may or may not be modified.
4654+
* Assume that the data is invalid.
4655+
*/
4656+
static Bool nxagentMergeScreenBoxes(nxagentScreenBoxes *all_boxes, nxagentScreenBoxes *screen_boxes, const XineramaScreenInfo *screen_info, const size_t screen_count) {
4657+
Bool ret = FALSE;
4658+
4659+
if ((!(all_boxes)) || (!(screen_boxes)) || (!(screen_info)) || (!(screen_count))) {
4660+
return(ret);
4661+
}
4662+
4663+
for (size_t i = 0; i < screen_count; ++i) {
4664+
/*
4665+
* Structure holding the box elements intersecting with
4666+
* the current screen.
4667+
*/
4668+
nxagentScreenBoxes *cur_screen_boxes = (screen_boxes + i);
4669+
xorg_list_init(&cur_screen_boxes);
4670+
4671+
nxagentScreenBoxesElem *cur_box = NULL;
4672+
xorg_list_for_each_entry(cur_box, all_boxes, entry) {
4673+
Bool cur_intersect = nxagentScreenBoxIntersectsScreen(cur_box, &(screen_info[i]));
4674+
4675+
if (cur_intersect) {
4676+
/*
4677+
* If a screen intersects the current box, we must:
4678+
* - create a deep copy of this box
4679+
* - add the copy to the screen boxes list
4680+
* - set the obsolete flag appropriately
4681+
* - start the low-level merge operation on the screen boxes list
4682+
*
4683+
* After this, assuming no error happened, the screen boxes list will
4684+
* contain only one element: a box containing the screen area.
4685+
*/
4686+
nxagentScreenBoxesElem *box_copy = calloc(sizeof(nxagentScreenBoxesElem), 1);
4687+
4688+
if (!(box_copy)) {
4689+
nxagentFreeScreenBoxes(all_boxes, TRUE);
4690+
return(ret);
4691+
}
4692+
4693+
/* Create new low-level BoxRec. */
4694+
box_copy->box = calloc(sizeof(BoxRec), 1);
4695+
4696+
if (!(box_copy->box)) {
4697+
nxagentFreeScreenBoxes(cur_box, TRUE);
4698+
nxagentFreeScreenBoxes(all_boxes, TRUE);
4699+
return(ret);
4700+
}
4701+
4702+
/* Copy BoxRec contents. */
4703+
memmove(box_copy->box, cur_box->box, sizeof(BoxRec));
4704+
4705+
box_copy->obsolete = TRUE;
4706+
4707+
for (size_t y = (i + 1); y < screen_count; ++y) {
4708+
if (nxagentScreenBoxIntersectsScreen(cur_box, &(screen_info[y]))) {
4709+
/* Protect box, if still needed later on after merging this set. */
4710+
box_copy->obsolete = FALSE;
4711+
break;
4712+
}
4713+
}
4714+
4715+
xorg_list_append(&(box_copy->entry), cur_screen_boxes);
4716+
}
4717+
}
4718+
4719+
/* Actually merge the boxes. */
4720+
if (!(nxagentMergeBoxes(all_boxes, cur_screen_boxes))) {
4721+
return(ret);
4722+
}
4723+
}
4724+
4725+
ret = TRUE;
4726+
4727+
return(ret);
4728+
}
4729+
43574730
/*
43584731
Destroy an output after removing it from any crtc that might reference it
43594732
*/

0 commit comments

Comments
 (0)