/****************************************************************************** * recordMyDesktop * ******************************************************************************* * * * Copyright (C) 2006,2007,2008 John Varouhakis * * * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * * * * * * For further information contact me at johnvarouhakis@gmail.com * ******************************************************************************/ #include "config.h" #include "rmd_rectinsert.h" #include "rmd_types.h" #include /** * Collide two rectangles and dictate most sane action for insertion, * as well as provide the updated rectangle(s) * \param xrect1 resident rectangle * * \param xrect2 New rectangle * * \param xrect_return Pointer to rectangles to be inserted * * \param nrects number of entries in xrect_return * * \retval 0 No collision * * \retval 1 xrect1 is covered by xrect2 * * \retval 2 xrect2 is covered by xrect1 * * \retval -1 xrect1 was broken (new is picked up in xrect_return) * * \retval -2 xrect2 was broken (new is picked up in xrect_return) * * \retval -10 Grouping the two rects is possible * */ static int rmdCollideRects(XRectangle *xrect1, XRectangle *xrect2, XRectangle xrect_return[], int *nrects) { //1 fits in 2 if((xrect1->x>=xrect2->x)&& (xrect1->x+xrect1->width<=xrect2->x+xrect2->width)&& (xrect1->y>=xrect2->y)&& (xrect1->y+xrect1->height<=xrect2->y+xrect2->height)){ *nrects=0; return 1; } //2 fits in 1 else if((xrect2->x>=xrect1->x)&& (xrect2->x+xrect2->width<=xrect1->x+xrect1->width)&& (xrect2->y>=xrect1->y)&& (xrect2->y+xrect2->height<=xrect1->y+xrect1->height)){ *nrects=0; return 2; } //no collision else if((xrect1->x+xrect1->widthx)|| (xrect2->x+xrect2->widthx)|| (xrect1->y+xrect1->heighty)|| (xrect2->y+xrect2->heighty)){ *nrects=0; return 0; } else{ //overlapping points are considered enclosed //this happens because libxdamage may generate many events for one change //and some of them may be in the the exact same region //so identical rects would be considered not colliding //in order though to avoid endless recursion on the rmdRectInsert //function should always start at the next element(which is logical since //if any rect makes it to a points none of it's part collides with previous //nodes on the list, too) int x1[2]={xrect1->x,xrect1->x+xrect1->width}; int y1[2]={xrect1->y,xrect1->y+xrect1->height}; int x2[2]={xrect2->x,xrect2->x+xrect2->width}; int y2[2]={xrect2->y,xrect2->y+xrect2->height}; int enclosed[2][4],tot1,tot2; enclosed[0][0]=(((x1[0]>=x2[0])&&(x1[0]<=x2[1])&& (y1[0]>=y2[0])&&(y1[0]<=y2[1]))?1:0); enclosed[0][1]=(((x1[1]>=x2[0])&&(x1[1]<=x2[1])&& (y1[0]>=y2[0])&&(y1[0]<=y2[1]))?1:0); enclosed[0][2]=(((x1[0]>=x2[0])&&(x1[0]<=x2[1])&& (y1[1]>=y2[0])&&(y1[1]<=y2[1]))?1:0); enclosed[0][3]=(((x1[1]>=x2[0])&&(x1[1]<=x2[1])&& (y1[1]>=y2[0])&&(y1[1]<=y2[1]))?1:0); enclosed[1][0]=(((x2[0]>=x1[0])&&(x2[0]<=x1[1])&& (y2[0]>=y1[0])&&(y2[0]<=y1[1]))?1:0); enclosed[1][1]=(((x2[1]>=x1[0])&&(x2[1]<=x1[1])&& (y2[0]>=y1[0])&&(y2[0]<=y1[1]))?1:0); enclosed[1][2]=(((x2[0]>=x1[0])&&(x2[0]<=x1[1])&& (y2[1]>=y1[0])&&(y2[1]<=y1[1]))?1:0); enclosed[1][3]=(((x2[1]>=x1[0])&&(x2[1]<=x1[1])&& (y2[1]>=y1[0])&&(y2[1]<=y1[1]))?1:0); tot1=enclosed[0][0]+enclosed[0][1]+enclosed[0][2]+enclosed[0][3]; tot2=enclosed[1][0]+enclosed[1][1]+enclosed[1][2]+enclosed[1][3]; if((tot1==2)&&(tot2==2)){//same width or height, which is the best case //group if((enclosed[1][0]&&enclosed[1][1])&& (xrect1->width==xrect2->width)){ *nrects=1; xrect_return[0].x = xrect1->x; xrect_return[0].y = xrect1->y; xrect_return[0].width = xrect1->width; xrect_return[0].height = xrect2->height + xrect2->y - xrect1->y; return -10; } else if((enclosed[1][0]&&enclosed[1][2])&& (xrect1->height==xrect2->height)){ *nrects=1; xrect_return[0].x = xrect1->x; xrect_return[0].y = xrect1->y; xrect_return[0].width = xrect2->width + xrect2->x - xrect1->x; xrect_return[0].height = xrect1->height; return -10; } else if((enclosed[1][3]&&enclosed[1][1])&& (xrect1->height==xrect2->height)){ *nrects=1; xrect_return[0].x = xrect2->x; xrect_return[0].y = xrect2->y; xrect_return[0].width = xrect1->width + xrect1->x - xrect2->x; xrect_return[0].height = xrect2->height; return -10; } else if((enclosed[1][3]&&enclosed[1][2])&& (xrect1->width==xrect2->width)){ *nrects=1; xrect_return[0].x = xrect2->x; xrect_return[0].y = xrect2->y; xrect_return[0].width = xrect2->width; xrect_return[0].height = xrect1->height + xrect1->y - xrect2->y; return -10; } //if control reaches here therewasn't a group and we go on } if(tot2==2){ //break rect2 xrect_return[0].x = xrect2->x; xrect_return[0].y = xrect2->y; xrect_return[0].width = xrect2->width; xrect_return[0].height = xrect2->height; *nrects=1; if(enclosed[1][0]&&enclosed[1][1]){ xrect_return[0].y = y1[1]; xrect_return[0].height -= y1[1] - y2[0]; } else if(enclosed[1][0]&&enclosed[1][2]){ xrect_return[0].x = x1[1]; xrect_return[0].width -= x1[1] - x2[0]; } else if(enclosed[1][3]&&enclosed[1][1]) xrect_return[0].width -= x2[1] - x1[0]; else if(enclosed[1][3]&&enclosed[1][2]) xrect_return[0].height -= y2[1] - y1[0]; return -2; } else if(tot1==2){ //if the first one breaks(which is already inserted) //then we reenter the part that was left and the one //that was to be inserted xrect_return[1].x = xrect2->x; xrect_return[1].y = xrect2->y; xrect_return[1].width = xrect2->width; xrect_return[1].height = xrect2->height; xrect_return[0].x = xrect1->x; xrect_return[0].y = xrect1->y; xrect_return[0].width = xrect1->width; xrect_return[0].height = xrect1->height; *nrects=1; if(enclosed[0][0]&&enclosed[0][1]){ xrect_return[0].y = y2[1]; xrect_return[0].height -= y2[1] - y1[0]; } else if(enclosed[0][0]&&enclosed[0][2]){ xrect_return[0].x = x2[1]; xrect_return[0].width -= x2[1] - x1[0]; } else if(enclosed[0][3]&&enclosed[0][1]) xrect_return[0].width -= x1[1] - x2[0]; else if(enclosed[0][3]&&enclosed[0][2]) xrect_return[0].height -= y1[1] - y2[0]; return -1; } else if(tot2==1){ //in which case there is also tot1==1 //but we rather not break that //break rect2 in two *nrects=2; if(enclosed[1][0]){ //first xrect_return[0].x = x1[1]; xrect_return[0].y = y2[0]; xrect_return[0].width = xrect2->width - x1[1] + x2[0]; xrect_return[0].height = xrect2->height; //second xrect_return[1].x = x2[0]; xrect_return[1].y = y1[1]; xrect_return[1].width = x1[1] - x2[0]; xrect_return[1].height = xrect2->height - y1[1] + y2[0]; } else if(enclosed[1][1]){ //first xrect_return[0].x = x2[0]; xrect_return[0].y = y2[0]; xrect_return[0].width = xrect2->width - x2[1] + x1[0]; xrect_return[0].height = xrect2->height; //second xrect_return[1].x = x1[0]; xrect_return[1].y = y1[1]; xrect_return[1].width = x2[1] - x1[0]; xrect_return[1].height = xrect2->height - y1[1] + y2[0]; } else if(enclosed[1][2]){ //first(same as [1][0]) xrect_return[0].x = x1[1]; xrect_return[0].y = y2[0]; xrect_return[0].width = xrect2->width - x1[1] + x2[0]; xrect_return[0].height = xrect2->height; //second xrect_return[1].x = x2[0]; xrect_return[1].y = y2[0]; xrect_return[1].width = x1[1] - x2[0]; xrect_return[1].height = xrect2->height - y2[1] + y1[0]; } else if(enclosed[1][3]){ //first(same as [1][1]) xrect_return[0].x = x2[0]; xrect_return[0].y = y2[0]; xrect_return[0].width = xrect2->width - x2[1] + x1[0]; xrect_return[0].height = xrect2->height; //second xrect_return[1].x = x1[0]; xrect_return[1].y = y2[0]; xrect_return[1].width = x2[1] - x1[0]; xrect_return[1].height = xrect2->height - y2[1] + y1[0]; } return -2; } else{//polygons collide but no point of one is in the other //so we just keep the two parts of rect2 that are outside rect1 //break rect2 in two //two cases: //rect2 crossing vertically rect1 //and rect2 crossing horizontally rect1 //The proper one can be found by simply checking x,y positions *nrects=2; if(xrect2->yy){ //common xrect_return[0].x = xrect_return[1].x = x2[0]; xrect_return[0].width = xrect_return[1].width = xrect2->width; xrect_return[0].y = y2[0]; xrect_return[0].height = xrect2->height - y2[1] + y1[0]; xrect_return[1].y = y1[1]; xrect_return[1].height = y2[1] - y1[1]; } else{ //common xrect_return[0].y = xrect_return[1].y = y2[0]; xrect_return[0].height = xrect_return[1].height = xrect2->height; xrect_return[0].x = x2[0]; xrect_return[0].width = xrect2->width - x2[1] + x1[0]; xrect_return[1].x = x1[1]; xrect_return[1].width = x2[1] - x1[1]; } return -2; } } } int rmdRectInsert(RectArea **root,XRectangle *xrect){ int total_insertions=0; RectArea *temp=NULL,*newnode=(RectArea *)malloc(sizeof(RectArea)); //align //we do need to know boundaries xrect->width+=(xrect->width%2)|(xrect->x%2); xrect->height+=(xrect->height%2)|(xrect->y%2); xrect->width+=(xrect->width%2); xrect->height+=(xrect->height%2); xrect->x-=xrect->x%2; xrect->y-=xrect->y%2; // fprintf(stderr," %d %d %d %d\n",xrect->x, newnode->rect.x=xrect->x; newnode->rect.y=xrect->y; newnode->rect.width=xrect->width; newnode->rect.height=xrect->height; newnode->prev=newnode->next=NULL; if(*root==NULL){ *root=newnode; total_insertions=1; } else{ XRectangle xrect_return[2]; int nrects=0,insert_ok=1,i=0; temp=*root; while(insert_ok){ //if something is broken list does not procceed //(except on -1 collres case) int collres = rmdCollideRects(&temp->rect, xrect, &xrect_return[0], &nrects); if((!collres)) insert_ok=1; else{ for(i=0;iprev!=NULL){//no root if(temp->next!=NULL){// RectArea *temp1=temp->next; temp->prev->next=temp->next; temp->next->prev=temp->prev; free(temp); if((xrect->width>0)&&(xrect->height>0)) total_insertions+=rmdRectInsert(&temp1,xrect); } else{ temp->prev->next=newnode; newnode->prev=temp->prev; total_insertions++; free(temp); } } else{//root if((*root)->next!=NULL){ (*root)=(*root)->next; (*root)->prev=NULL; if((xrect->width>0)&&(xrect->height>0)) total_insertions+=rmdRectInsert(root,xrect); } else if((xrect->width>0)&&(xrect->height>0)){ *root=newnode; total_insertions++; } else{ *root=NULL; total_insertions++; } free(temp); } break; case 2://done,area is already covered free(newnode); break; case -1://current node is broken and reinserted //(in same pos) //newnode is also reinserted if (xrect_return[0].width > 0 && xrect_return[0].height > 0) { temp->rect.x = xrect_return[0].x; temp->rect.y = xrect_return[0].y; temp->rect.width = xrect_return[0].width; temp->rect.height = xrect_return[0].height; if(temp->next==NULL){ temp->next=newnode; newnode->prev=temp; total_insertions++; } else{ insert_ok=1; } } else{//it might happen that the old and now broken node //is of zero width or height //(so it isn't reinserted) if((temp->prev==NULL)&&(temp->next!=NULL)){ *root=(*root)->next; (*root)->prev=NULL; } else if((temp->next==NULL)&&(temp->prev!=NULL)){ temp->prev->next=newnode; newnode->prev=temp->prev; } else if((temp->next==NULL)&&(temp->prev==NULL)) (*root)=newnode; else{ total_insertions--; temp->next->prev=temp->prev; temp->prev->next=temp->next; total_insertions+=rmdRectInsert(&temp->next, xrect); } free(temp); } break; case -2://new is broken and reinserted if(temp->next==NULL){ total_insertions+=nrects; newnode->rect.x = xrect_return[0].x; newnode->rect.y = xrect_return[0].y; newnode->rect.width = xrect_return[0].width; newnode->rect.height = xrect_return[0].height; temp->next=newnode; newnode->prev=temp; if(nrects>1){ RectArea *newnode1= (RectArea *)malloc(sizeof(RectArea)); newnode1->rect.x = xrect_return[1].x; newnode1->rect.y = xrect_return[1].y; newnode1->rect.width = xrect_return[1].width; newnode1->rect.height = xrect_return[1].height; newnode->next=newnode1; newnode1->prev=newnode; newnode1->next=NULL; } } else{ for(i=0;i 0 && xrect_return[i].height > 0) total_insertions+= rmdRectInsert(&temp->next, &xrect_return[i]); } } break; case -10://grouped if(temp->prev==NULL){ if(temp->next==NULL){//empty list newnode->rect.x = xrect_return[0].x; newnode->rect.y = xrect_return[0].y; newnode->rect.width = xrect_return[0].width; newnode->rect.height = xrect_return[0].height; *root=newnode; free(temp); } else{ total_insertions--; *root=temp->next; (*root)->prev=NULL; free(temp); if(xrect_return[0].width > 0 && xrect_return[0].height > 0) total_insertions+= rmdRectInsert(root, &xrect_return[0]); } } else if(temp->next==NULL){//last, enter anyway newnode->rect.x = xrect_return[0].x; newnode->rect.y = xrect_return[0].y; newnode->rect.width = xrect_return[0].width; newnode->rect.height = xrect_return[0].height; temp->prev->next=newnode; newnode->prev=temp->prev; free(temp); } else{//remove node and reinsert, starting where we were total_insertions--; RectArea *temp1=temp->next; temp->prev->next=temp->next; temp->next->prev=temp->prev; free(temp); if(xrect_return[0].width > 0 && xrect_return[0].height > 0) total_insertions+= rmdRectInsert(&temp1, &xrect_return[0]); } break; } } if(insert_ok){ if(temp->next==NULL){ temp->next=newnode; newnode->prev=temp; total_insertions++; break; } else{ temp=temp->next; } } else{ break; } }; } return total_insertions; } void rmdClearList(RectArea **root){ RectArea *temp; temp=*root; if(temp!=NULL){ while(temp->next!=NULL){ temp=temp->next; free(temp->prev); } free(temp); *root=NULL; } }