1 /*
2  * Copyright (C) 2002 Sasha Vasko <sasha at aftercode.net>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  */
19 
20 #undef LOCAL_DEBUG
21 #undef DO_CLOCKING
22 
23 #include <string.h>
24 
25 #include "../configure.h"
26 
27 #include "asapp.h"
28 #include "../libAfterImage/afterimage.h"
29 #include "screen.h"
30 #include "shape.h"
31 #include "canvas.h"
32 
33 
34 inline Bool
get_current_canvas_geometry(ASCanvas * pc,int * px,int * py,unsigned int * pwidth,unsigned int * pheight,unsigned int * pbw)35 get_current_canvas_geometry (ASCanvas * pc, int *px, int *py,
36 														 unsigned int *pwidth, unsigned int *pheight,
37 														 unsigned int *pbw)
38 {
39 	Window wdumm;
40 	unsigned int udumm;
41 	int dumm;
42 
43 	if (pc == NULL)
44 		return False;
45 
46 	if (px == NULL)
47 		px = &dumm;
48 	if (py == NULL)
49 		py = &dumm;
50 	if (pwidth == NULL)
51 		pwidth = &udumm;
52 	if (pheight == NULL)
53 		pheight = &udumm;
54 	if (pbw == NULL)
55 		pbw = &udumm;
56 
57 	return (XGetGeometry
58 					(dpy, pc->w, &wdumm, px, py, pwidth, pheight, pbw, &udumm) != 0);
59 }
60 
set_canvas_shape_to_rectangle(ASCanvas * pc)61 static void set_canvas_shape_to_rectangle (ASCanvas * pc)
62 {
63 #ifdef SHAPE
64 	unsigned int width, height, bw;
65 	XRectangle rect;
66 
67 	rect.x = 0;
68 	rect.y = 0;
69 #ifdef STRICT_GEOMETRY
70 	get_current_canvas_geometry (pc, NULL, NULL, &width, &height, &bw);
71 #else
72 	width = pc->width;
73 	height = pc->height;
74 	bw = pc->bw;
75 #endif
76 	rect.width = width + bw * 2;
77 	rect.height = height + bw * 2;
78 	LOCAL_DEBUG_OUT ("XShapeCombineRectangles(%lX) (%dx%d%+d%+d)", pc->w,
79 									 rect.width, rect.height, -bw, -bw);
80 	XShapeCombineRectangles (dpy, pc->w, ShapeBounding, -bw, -bw, &rect, 1,
81 													 ShapeSet, Unsorted);
82 	set_flags (pc->state, CANVAS_SHAPE_SET);
83 #endif
84 }
85 
set_canvas_shape_to_nothing(ASCanvas * pc)86 static void set_canvas_shape_to_nothing (ASCanvas * pc)
87 {
88 #ifdef SHAPE
89 	LOCAL_DEBUG_OUT ("XShapeCombineMask(%lX) (clearing mask)", pc->w);
90 	XShapeCombineMask (dpy, pc->w, ShapeBounding, 0, 0, None, ShapeSet);
91 	clear_flags (pc->state, CANVAS_SHAPE_SET);
92 #endif
93 	if (pc->shape)
94 		destroy_shape (&pc->shape);
95 }
96 
97 
98 
99 
100 /********************************************************************/
101 /* ASCanvas :                                                       */
102 /********************************************************************/
refresh_canvas_config(ASCanvas * pc)103 static ASFlagType refresh_canvas_config (ASCanvas * pc)
104 {
105 	ASFlagType changed = 0;
106 
107 	if (pc && pc->w != None) {
108 		int root_x = pc->root_x, root_y = pc->root_y, dumm;
109 		unsigned int width = pc->width, height = pc->height, udumm;
110 		Window wdumm;
111 		unsigned int bw;
112 
113 		XTranslateCoordinates (dpy, pc->w, ASDefaultRoot, 0, 0, &root_x,
114 													 &root_y, &wdumm);
115 		XGetGeometry (dpy, pc->w, &wdumm, &dumm, &dumm, &udumm, &udumm, &bw,
116 									&udumm);
117 		root_x -= bw;
118 		root_y -= bw;
119 		if (root_x != pc->root_x)
120 			set_flags (changed, CANVAS_X_CHANGED);
121 		if (root_y != pc->root_y)
122 			set_flags (changed, CANVAS_Y_CHANGED);
123 		pc->root_x = root_x;
124 		pc->root_y = root_y;
125 		pc->bw = bw;
126 
127 		if (!get_drawable_size (pc->w, &width, &height))
128 			return 0;									/* drawable is bad */
129 		if (width != pc->width)
130 			set_flags (changed, CANVAS_WIDTH_CHANGED);
131 		if (height != pc->height)
132 			set_flags (changed, CANVAS_HEIGHT_CHANGED);
133 
134 		clear_flags (pc->state, CANVAS_CONFIG_INVALID);
135 
136 		if (width != pc->width || height != pc->height) {
137 			destroy_visual_pixmap (ASDefaultVisual, &(pc->saved_canvas));
138 			destroy_visual_pixmap (ASDefaultVisual, &(pc->canvas));
139 
140 			if (pc->saved_shape)
141 				destroy_shape (&(pc->saved_shape));
142 
143 			if (pc->shape) {
144 				destroy_shape (&(pc->shape));
145 			}
146 			set_flags (pc->state, CANVAS_DIRTY | CANVAS_OUT_OF_SYNC);
147 			pc->width = width;
148 			pc->height = height;
149 		}
150 	}
151 	return changed;
152 }
153 
get_canvas_canvas(ASCanvas * pc)154 Pixmap get_canvas_canvas (ASCanvas * pc)
155 {
156 	if (pc == NULL)
157 		return None;
158 	if (get_flags (pc->state, CANVAS_CONTAINER))
159 		return None;
160 	LOCAL_DEBUG_CALLER_OUT ("ASCanvas(%p)->canvas(%lX)", pc, pc->canvas);
161 	if (pc->canvas == None) {
162 		pc->canvas =
163 				create_visual_pixmap (ASDefaultVisual, ASDefaultRoot, pc->width,
164 															pc->height, 0);
165 		set_flags (pc->state, CANVAS_DIRTY | CANVAS_OUT_OF_SYNC);
166 		XSetWindowBackgroundPixmap (dpy, pc->w, pc->canvas);
167 	}
168 	return pc->canvas;
169 }
170 
create_ascanvas(Window w)171 ASCanvas *create_ascanvas (Window w)
172 {
173 	ASCanvas *pc = NULL;
174 
175 	if (w) {
176 		pc = safecalloc (1, sizeof (ASCanvas));
177 		pc->w = w;
178 		refresh_canvas_config (pc);
179 		LOCAL_DEBUG_CALLER_OUT
180 				("<<#########>>canvas %p created for w: %lX; geom = %dx%d%+d%+d, bw = %d",
181 				 pc, pc->w, pc->width, pc->height, pc->root_x, pc->root_y, pc->bw);
182 	}
183 	return pc;
184 }
185 
create_ascanvas_container(Window w)186 ASCanvas *create_ascanvas_container (Window w)
187 {
188 	ASCanvas *pc = NULL;
189 
190 	if (w) {
191 		pc = safecalloc (1, sizeof (ASCanvas));
192 		pc->w = w;
193 		pc->state = CANVAS_CONTAINER;
194 		refresh_canvas_config (pc);
195 		LOCAL_DEBUG_CALLER_OUT
196 				("<<#########>>container canvas %p created for w: %lX; geom = %dx%d%+d%+d, bw = %d",
197 				 pc, pc->w, pc->width, pc->height, pc->root_x, pc->root_y, pc->bw);
198 	}
199 	return pc;
200 }
201 
destroy_ascanvas(ASCanvas ** pcanvas)202 void destroy_ascanvas (ASCanvas ** pcanvas)
203 {
204 	if (pcanvas) {
205 		ASCanvas *pc = *pcanvas;
206 
207 		LOCAL_DEBUG_CALLER_OUT
208 				("<<#########>>destroying canvas %p for window %lX", pc,
209 				 pc ? pc->w : None);
210 		if (pc) {
211 			destroy_visual_pixmap (ASDefaultVisual, &(pc->saved_canvas));
212 			destroy_visual_pixmap (ASDefaultVisual, &(pc->canvas));
213 			LOCAL_DEBUG_OUT ("saved_shape = %p, shape = %p", pc->saved_shape,
214 											 pc->shape);
215 			if (pc->saved_shape && pc->saved_shape != pc->shape)
216 				destroy_shape (&(pc->saved_shape));
217 			if (pc->shape)
218 				destroy_shape (&(pc->shape));
219 			memset (pc, 0x00, sizeof (ASCanvas));
220 			free (pc);
221 		}
222 		*pcanvas = NULL;
223 	}
224 }
225 
handle_canvas_config(ASCanvas * canvas)226 ASFlagType handle_canvas_config (ASCanvas * canvas)
227 {
228 	ASFlagType res;
229 
230 	LOCAL_DEBUG_CALLER_OUT
231 			("canvas(%p)->window(%lx)->orig_geom(%ux%u%+d%+d)", canvas,
232 			 canvas->w, canvas->width, canvas->height, canvas->root_x,
233 			 canvas->root_y);
234 	res = refresh_canvas_config (canvas);
235 	LOCAL_DEBUG_CALLER_OUT
236 			("canvas(%p)->window(%lx)->new__geom(%ux%u%+d%+d)->change(0x%lX)",
237 			 canvas, canvas->w, canvas->width, canvas->height, canvas->root_x,
238 			 canvas->root_y, res);
239 	return res;
240 }
241 
invalidate_canvas_config(ASCanvas * pc)242 void invalidate_canvas_config (ASCanvas * pc)
243 {
244 	if (pc) {
245 		if (get_flags (pc->state, CANVAS_CONFIG_INVALID))
246 			return;
247 
248 		LOCAL_DEBUG_OUT ("resizing to %dx%d", pc->width + 1, pc->height + 1);
249 		XResizeWindow (dpy, pc->w, pc->width + 1, pc->height + 1);
250 #ifdef SHAPE
251 		if (!get_flags (pc->state, CANVAS_CONTAINER)
252 				&& get_flags (pc->state, CANVAS_SHAPE_SET))
253 			set_canvas_shape_to_nothing (pc);
254 #endif
255 
256 		pc->width = 0;
257 		pc->height = 0;
258 		destroy_visual_pixmap (ASDefaultVisual, &(pc->canvas));
259 		destroy_visual_pixmap (ASDefaultVisual, &(pc->saved_canvas));
260 		if (pc->shape)
261 			destroy_shape (&(pc->shape));
262 		if (pc->saved_shape)
263 			destroy_shape (&(pc->saved_shape));
264 		set_flags (pc->state,
265 							 CANVAS_DIRTY | CANVAS_OUT_OF_SYNC |
266 							 CANVAS_MASK_OUT_OF_SYNC);
267 		set_flags (pc->state, CANVAS_CONFIG_INVALID);
268 	}
269 }
270 
271 Bool
get_canvas_position(ASCanvas * pc,Window * pparent,int * px,int * py,unsigned int * pbw)272 get_canvas_position (ASCanvas * pc, Window * pparent, int *px, int *py,
273 										 unsigned int *pbw)
274 {
275 	Window wdumm;
276 	int dumm;
277 	unsigned int udumm;
278 
279 	if (pparent == NULL)
280 		pparent = &wdumm;
281 	if (px == NULL)
282 		px = &dumm;
283 	if (py == NULL)
284 		py = &dumm;
285 	if (pbw == NULL)
286 		pbw = &udumm;
287 	if (pc)
288 		if (XGetGeometry
289 				(dpy, pc->w, pparent, px, py, &udumm, &udumm, pbw, &udumm))
290 			return True;
291 	return False;
292 }
293 
294 Bool
make_canvas_rectangle(ASCanvas * pc,ASImage * im,int x,int y,int * cx,int * cy,int * cwidth,int * cheight)295 make_canvas_rectangle (ASCanvas * pc, ASImage * im, int x, int y, int *cx,
296 											 int *cy, int *cwidth, int *cheight)
297 {
298 	if (pc == NULL || im == NULL || cx == NULL || cy == NULL
299 			|| cwidth == NULL || cheight == NULL)
300 		return False;
301 	*cwidth = im->width;
302 	*cheight = im->height;
303 	*cx = x;
304 	*cy = y;
305 	if (x + *cwidth <= 0 || x > pc->width)
306 		return False;
307 	if (y + *cheight <= 0 || y > pc->height)
308 		return False;
309 
310 	if (*cx < 0) {
311 		*cwidth += *cx;
312 		*cx = 0;
313 	}
314 	if (*cx + *cwidth > pc->width)
315 		*cwidth = pc->width - *cx;
316 	if (*cy < 0) {
317 		*cheight += *cy;
318 		*cy = 0;
319 	}
320 	if (*cy + *cheight > pc->height)
321 		*cheight = pc->height - *cy;
322 	return True;
323 }
324 
325 
draw_canvas_image(ASCanvas * pc,ASImage * im,int x,int y)326 Bool draw_canvas_image (ASCanvas * pc, ASImage * im, int x, int y)
327 {
328 	Pixmap p;
329 	int real_x, real_y;
330 	int width, height;
331 	Bool done = False;
332 
333 	LOCAL_DEBUG_CALLER_OUT ("pc(%p)->im(%p)->x(%d)->y(%d)", pc, im, x, y);
334 	if (im == NULL || pc == NULL)
335 		return False;
336 	if ((p = get_canvas_canvas (pc)) == None)
337 		return False;
338 
339 	if (!make_canvas_rectangle
340 			(pc, im, x, y, &real_x, &real_y, &width, &height))
341 		return False;
342 
343 	LOCAL_DEBUG_OUT ("drawing image %dx%d at %dx%d%+d%+d", im->width,
344 									 im->height, width, height, real_x, real_y);
345 	if (get_flags (ASDefaultVisual->glx_support, ASGLX_UseForImageTx))
346 		done = asimage2drawable_gl (ASDefaultVisual, p, im,
347 																real_x - x, real_y - y, real_x, real_y,
348 																width, height, pc->width, pc->height,
349 																False);
350 	if (!done)
351 		done =
352 				asimage2drawable (ASDefaultVisual, p, im, ASDefaultDrawGC,
353 													real_x - x, real_y - y, real_x, real_y, width,
354 													height, True);
355 
356 	if (done) {
357 		set_flags (pc->state, CANVAS_OUT_OF_SYNC);
358 		XClearArea (dpy, pc->w, real_x, real_y, width, height, True);
359 		XSync (dpy, False);
360 	}
361 	return done;
362 }
363 
364 void
fill_canvas_mask(ASCanvas * pc,int win_x,int win_y,int width,int height)365 fill_canvas_mask (ASCanvas * pc, int win_x, int win_y, int width,
366 									int height)
367 {
368 	int real_x, real_y;
369 	int real_width, real_height;
370 
371 	if (pc == NULL)
372 		return;
373 
374 	if (pc->shape != None && !get_flags (pc->state, CANVAS_CONTAINER)) {
375 		real_x = MAX (win_x, 0);
376 		real_y = MAX (win_y, 0);
377 		real_width = width - (real_x - win_x);
378 		real_height = height - (real_y - win_y);
379 		if (real_width > 0 && real_height > 0 && real_x < pc->width
380 				&& real_y < pc->height) {
381 			int res;
382 			XRectangle rect;
383 
384 			rect.x = real_x;
385 			rect.y = real_y;
386 			rect.width = real_width;
387 			rect.height = real_height;
388 			LOCAL_DEBUG_OUT ("filling mask at %dx%d%+d%+d", rect.width,
389 											 rect.height, rect.x, rect.y);
390 			res =
391 					add_shape_rectangles (pc->shape, &rect, 1, 0, 0, pc->width,
392 																pc->height);
393 			if (res)
394 				set_flags (pc->state, CANVAS_MASK_OUT_OF_SYNC);
395 		}
396 	}
397 }
398 
399 
draw_canvas_mask(ASCanvas * pc,ASImage * im,int x,int y)400 Bool draw_canvas_mask (ASCanvas * pc, ASImage * im, int x, int y)
401 {
402 	XRectangle *rects;
403 	unsigned int rects_count = 0;
404 	Bool res = True;
405 	int real_x, real_y;
406 	int width, height;
407 
408 	if (im == NULL || pc == NULL)
409 		return False;
410 
411 	if (!make_canvas_rectangle
412 			(pc, im, x, y, &real_x, &real_y, &width, &height))
413 		return False;
414 
415 	rects = get_asimage_channel_rects (im, IC_ALPHA, 10, &rects_count);
416 	LOCAL_DEBUG_OUT ("%d rectangles generated", rects_count);
417 
418 	if (pc->shape == NULL)
419 		pc->shape = create_shape ();
420 
421 	if (rects != NULL && rects_count > 0) {
422 		res =
423 				add_shape_rectangles (pc->shape, rects, rects_count, real_x,
424 															real_y, pc->width + pc->bw,
425 															pc->height + pc->bw);
426 		free (rects);
427 	}
428 
429 	if (res)
430 		set_flags (pc->state, CANVAS_MASK_OUT_OF_SYNC);
431 	return res;
432 }
433 
434 inline Bool
get_current_canvas_size(ASCanvas * pc,unsigned int * pwidth,unsigned int * pheight)435 get_current_canvas_size (ASCanvas * pc, unsigned int *pwidth,
436 												 unsigned int *pheight)
437 {
438 	if (pc == NULL)
439 		return False;
440 
441 	*pwidth = pc->width;
442 	*pheight = pc->height;
443 	if (get_flags (pc->state, CANVAS_CONFIG_INVALID))
444 		return get_drawable_size (pc->w, pwidth, pheight);
445 	return True;
446 }
447 
448 
449 
update_canvas_display_mask(ASCanvas * pc,Bool force)450 void update_canvas_display_mask (ASCanvas * pc, Bool force)
451 {
452 #ifdef SHAPE
453 	LOCAL_DEBUG_CALLER_OUT ("canvas(%p)", pc);
454 	if (pc == NULL)
455 		return;
456 	LOCAL_DEBUG_OUT ("window(%lx)->canvas_pixmap(%lx)->size(%dx%d)", pc->w,
457 									 pc->canvas, pc->width, pc->height);
458 	if (pc->w != None) {
459 		if (force || !get_flags (pc->state, CANVAS_CONTAINER)) {
460 			if (pc->shape) {
461 				LOCAL_DEBUG_OUT
462 						("XShapeCombineREctangles(%lX)set canvas mask to %d rectangles",
463 						 pc->w, PVECTOR_USED (pc->shape));
464 				if (PVECTOR_USED (pc->shape) > 0)
465 					XShapeCombineRectangles (dpy, pc->w, ShapeBounding, 0, 0,
466 																	 PVECTOR_HEAD (XRectangle, pc->shape),
467 																	 PVECTOR_USED (pc->shape), ShapeSet,
468 																	 Unsorted);
469 				else {									/* we are still shaped - but completely opaque */
470 
471 					XRectangle nothing;
472 
473 					nothing.x = pc->width / 2;
474 					nothing.y = pc->height / 2;
475 					nothing.width = nothing.height = 1;
476 					XShapeCombineRectangles (dpy, pc->w, ShapeBounding, 0, 0,
477 																	 &nothing, 1, ShapeSet, Unsorted);
478 				}
479 				if (pc->bw > 0) {
480 					XRectangle border[4];
481 
482 					border[0].x = -pc->bw;
483 					border[0].y = -pc->bw;
484 					border[0].width = pc->width + pc->bw * 2;
485 					border[0].height = pc->bw;
486 					border[1].x = -pc->bw;
487 					border[1].y = -pc->bw;
488 					border[1].width = pc->bw;
489 					border[1].height = pc->height + pc->bw * 2;
490 					border[2].x = -pc->bw;
491 					border[2].y = pc->height;
492 					border[2].width = pc->width + pc->bw * 2;
493 					border[2].height = pc->bw;
494 					border[3].x = pc->width;
495 					border[3].y = -pc->bw;
496 					border[3].width = pc->bw;
497 					border[3].height = pc->height + pc->bw * 2;
498 					XShapeCombineRectangles (dpy, pc->w, ShapeBounding, 0, 0,
499 																	 &(border[0]), 4, ShapeUnion, Unsorted);
500 				}
501 				set_flags (pc->state, CANVAS_SHAPE_SET);
502 			} else if (get_flags (pc->state, CANVAS_SHAPE_SET))
503 				set_canvas_shape_to_nothing (pc);
504 			XSync (dpy, False);
505 			clear_flags (pc->state, CANVAS_MASK_OUT_OF_SYNC);
506 		}
507 	}
508 #endif
509 }
510 
511 
512 #ifdef TRACE_update_canvas_display
513 #undef update_canvas_display
514 void update_canvas_display (ASCanvas * pc);
515 void
trace_update_canvas_display(ASCanvas * pc,const char * file,int line)516 trace_update_canvas_display (ASCanvas * pc, const char *file, int line)
517 {
518 	fprintf (stderr, "D>%s(%d):update_canvas_display(%p)\n", file, line, pc);
519 	update_canvas_display (pc);
520 }
521 #endif
522 
523 
update_canvas_display(ASCanvas * pc)524 void update_canvas_display (ASCanvas * pc)
525 {
526 	LOCAL_DEBUG_CALLER_OUT ("canvas(%p)", pc);
527 	if (pc == NULL)
528 		return;
529 
530 	LOCAL_DEBUG_OUT ("window(%lx)->canvas_pixmap(%lx)->size(%dx%d)", pc->w,
531 									 pc->canvas, pc->width, pc->height);
532 	if (pc && pc->w != None) {
533 		if (!get_flags (pc->state, CANVAS_CONTAINER)) {
534 			if (pc->canvas) {
535 #ifdef SHAPE
536 				update_canvas_display_mask (pc, False);
537 #endif
538 				XSetWindowBackgroundPixmap (dpy, pc->w, pc->canvas);
539 				XClearWindow (dpy, pc->w);
540 
541 				XSync (dpy, False);
542 				clear_flags (pc->state,
543 										 CANVAS_DIRTY | CANVAS_OUT_OF_SYNC |
544 										 CANVAS_MASK_OUT_OF_SYNC);
545 			}
546 		}
547 	}
548 }
549 
invalidate_canvas_save(ASCanvas * pc)550 void invalidate_canvas_save (ASCanvas * pc)
551 {
552 	if (pc) {
553 		destroy_visual_pixmap (ASDefaultVisual, &(pc->saved_canvas));
554 		destroy_shape (&(pc->saved_shape));
555 	}
556 }
557 
558 
restore_canvas(ASCanvas * pc)559 Bool restore_canvas (ASCanvas * pc)
560 {
561 	if (pc) {
562 		if (pc->saved_canvas == None)
563 			return False;
564 
565 		destroy_visual_pixmap (ASDefaultVisual, &(pc->canvas));
566 		pc->canvas = pc->saved_canvas;
567 		pc->saved_canvas = None;
568 
569 
570 		destroy_shape (&(pc->shape));
571 		pc->shape = pc->saved_shape;
572 		pc->saved_shape = NULL;
573 
574 		update_canvas_display (pc);
575 		return True;
576 	}
577 	return False;
578 }
579 
save_canvas(ASCanvas * pc)580 Bool save_canvas (ASCanvas * pc)
581 {
582 	if (pc) {
583 		destroy_visual_pixmap (ASDefaultVisual, &(pc->saved_canvas));
584 		pc->saved_canvas = pc->canvas;
585 		pc->canvas = None;
586 
587 		destroy_shape (&(pc->saved_shape));
588 		pc->saved_shape = pc->shape;
589 		pc->shape = NULL;
590 
591 		set_flags (pc->state, CANVAS_MASK_OUT_OF_SYNC | CANVAS_OUT_OF_SYNC);
592 		return (pc->saved_canvas != None);
593 	}
594 	return False;
595 }
596 
swap_save_canvas(ASCanvas * pc)597 Bool swap_save_canvas (ASCanvas * pc)
598 {
599 	Pixmap tmp_canvas;
600 	struct ASVector *tmp_shape;
601 
602 	if (pc) {
603 		tmp_canvas = pc->saved_canvas;
604 		pc->saved_canvas = pc->canvas;
605 		pc->canvas = tmp_canvas;
606 
607 		tmp_shape = pc->saved_shape;
608 		pc->saved_shape = pc->shape;
609 		pc->shape = tmp_shape;
610 
611 		set_flags (pc->state, CANVAS_MASK_OUT_OF_SYNC | CANVAS_OUT_OF_SYNC);
612 		return (pc->canvas != None);
613 	}
614 	return False;
615 
616 }
617 
618 
clear_canvas_shape(ASCanvas * pc,Bool force_for_container)619 void clear_canvas_shape (ASCanvas * pc, Bool force_for_container)
620 {
621 	LOCAL_DEBUG_CALLER_OUT ("canvas(%p)", pc);
622 	if (pc == NULL)
623 		return;
624 
625 	LOCAL_DEBUG_OUT ("window(%lx)->canvas_pixmap(%lx)->size(%dx%d)", pc->w,
626 									 pc->canvas, pc->width, pc->height);
627 	if (pc->w != None) {
628 		if (!get_flags (pc->state, CANVAS_CONTAINER) || force_for_container) {
629 #ifdef SHAPE
630 			if (pc->canvas && get_flags (pc->state, CANVAS_SHAPE_SET))
631 				set_canvas_shape_to_rectangle (pc);	/* just in case  */
632 			set_canvas_shape_to_nothing (pc);
633 #endif
634 			if (pc->shape)
635 				destroy_shape (&pc->shape);
636 			set_flags (pc->state, CANVAS_DIRTY | CANVAS_OUT_OF_SYNC);
637 		}
638 	}
639 }
640 
check_canvas_shaped(ASCanvas * pc)641 Bool check_canvas_shaped (ASCanvas * pc)
642 {
643 	Bool boundingShaped = False;
644 
645 #ifdef SHAPE
646 	if (pc) {
647 		int dumm;
648 		unsigned udumm;
649 
650 		XShapeQueryExtents (dpy, pc->w,
651 												&boundingShaped, &dumm, &dumm, &udumm, &udumm,
652 												&dumm, &dumm, &dumm, &udumm, &udumm);
653 	}
654 #endif
655 	return boundingShaped;
656 }
657 
refresh_container_shape(ASCanvas * pc)658 Bool refresh_container_shape (ASCanvas * pc)
659 {
660 	int res = False;
661 
662 	if (pc && get_flags (pc->state, CANVAS_CONTAINER)) {
663 		int count, order;
664 		XRectangle *rects;
665 
666 #ifdef SHAPE
667 		rects =
668 				XShapeGetRectangles (dpy, pc->w, ShapeBounding, &count, &order);
669 #endif
670 		if (rects) {
671 			unsigned int curr_width = 1, curr_height = 1;
672 
673 			if (pc->shape == NULL)
674 				pc->shape = create_shape ();
675 			else
676 				flush_vector (pc->shape);
677 			get_drawable_size (pc->w, &curr_width, &curr_height);
678 			res =
679 					add_shape_rectangles (pc->shape, rects, count, 0, 0,
680 																curr_width + pc->bw * 2,
681 																curr_height + pc->bw * 2);
682 			XFree (rects);
683 
684 			if (res)
685 				set_flags (pc->state, CANVAS_MASK_OUT_OF_SYNC);
686 		} else if (pc->shape) {
687 			destroy_shape (&(pc->shape));
688 			set_flags (pc->state, CANVAS_MASK_OUT_OF_SYNC);
689 			res = True;
690 		}
691 	}
692 	return res;
693 }
694 
695 
696 Bool
combine_canvas_shape_at_geom(ASCanvas * parent,ASCanvas * child,int child_x,int child_y,int child_width,int child_height,int child_bw)697 combine_canvas_shape_at_geom (ASCanvas * parent, ASCanvas * child,
698 															int child_x, int child_y, int child_width,
699 															int child_height, int child_bw)
700 {
701 	int res = False;
702 
703 #ifdef SHAPE
704 	LOCAL_DEBUG_OUT ("parent(%p),child(%p)", parent, child);
705 	if (parent && child) {
706 		unsigned int parent_width, parent_height;
707 
708 		LOCAL_DEBUG_OUT ("parent->shape(%p)", parent->shape);
709 		if (parent->shape == NULL)
710 			return False;
711 
712 		LOCAL_DEBUG_OUT ("child->shape(%p)", child->shape);
713 		if (child->shape != NULL && PVECTOR_USED (child->shape) == 0) {
714 			LOCAL_DEBUG_OUT ("child->shape has no rectangles%s", "");
715 			return False;							/* child has an empty shape */
716 		}
717 #ifdef STRICT_GEOMETRY
718 		get_current_canvas_size (parent, &parent_width, &parent_height);
719 #else
720 		parent_width = parent->width;
721 		parent_height = parent->height;
722 #endif
723 
724 		if (child_x > parent_width || child_y > parent_height ||
725 				child_x + child_width <= 0 || child_y + child_height <= 0) {
726 #ifdef LOCAL_DEBUG
727 			if (get_flags (child->state, CANVAS_CONTAINER))
728 				LOCAL_DEBUG_OUT
729 						("container shape ignored - out of bounds: %dx%d%+d%+d parents: %dx%d%+d%+d",
730 						 child_width, child_height, child_x, child_y, parent_width,
731 						 parent_height, parent->root_x, parent->root_y);
732 #endif
733 			return False;
734 		}
735 #if 1
736 		if (child->shape && PVECTOR_USED (child->shape) > 0) {
737 			LOCAL_DEBUG_OUT ("adding %d child's shape rectangles",
738 											 PVECTOR_USED (child->shape));
739 #if defined(LOCAL_DEBUG) && !defined(NO_DEBUG_OUTPUT)
740 			{
741 				int i, max_i = PVECTOR_USED (child->shape);
742 				XRectangle *r = PVECTOR_HEAD (XRectangle, child->shape);
743 
744 				for (i = 0; i < max_i; ++i)
745 					fprintf (stderr, "\t\t r[%d] = %dx%d%+d%+d\n", i, r[i].width,
746 									 r[i].height, r[i].x, r[i].y);
747 			}
748 			LOCAL_DEBUG_OUT ("child geom = %dx%d%+d%+d, bw = %d", child_width,
749 											 child_height, child_x, child_y, child_bw);
750 #endif
751 			res =
752 					add_shape_rectangles (parent->shape,
753 																PVECTOR_HEAD (XRectangle, child->shape),
754 																PVECTOR_USED (child->shape),
755 																child_x + child_bw, child_y + child_bw,
756 																parent_width, parent_height);
757 			if (res && child_bw > 0) {
758 				XRectangle border[4];
759 
760 				border[0].x = 0;
761 				border[0].y = 0;
762 				border[0].width = child_width + child_bw;
763 				border[0].height = child_bw;
764 				border[1].x = 0;
765 				border[1].y = child_bw;
766 				border[1].width = child_bw;
767 				border[1].height = child_height;
768 				border[2].x = child_width + child_bw;
769 				border[2].y = 0;
770 				border[2].width = child_bw;
771 				border[2].height = child_height + child_bw * 2;
772 				border[3].x = 0;
773 				border[3].y = child_height + child_bw;
774 				border[3].width = child_width + child_bw * 2;
775 				border[3].height = child_bw;
776 				res =
777 						add_shape_rectangles (parent->shape, &(border[0]), 4, child_x,
778 																	child_y, parent_width, parent_height);
779 			}
780 		} else
781 #endif
782 		{
783 			XRectangle rect;
784 
785 			rect.x = child_x;
786 			rect.y = child_y;
787 			rect.width = child_width + child_bw * 2;
788 			rect.height = child_height + child_bw * 2;
789 			LOCAL_DEBUG_OUT
790 					("adding child's shape as whole rectangle: %dx%d%+d%+d",
791 					 rect.width, rect.height, rect.x, rect.y);
792 
793 			res =
794 					add_shape_rectangles (parent->shape, &rect, 1, 0, 0,
795 																parent_width + parent->bw * 2,
796 																parent_height + parent->bw * 2);
797 		}
798 	}
799 #endif
800 	return res;
801 }
802 
combine_canvas_shape(ASCanvas * parent,ASCanvas * child)803 Bool combine_canvas_shape (ASCanvas * parent, ASCanvas * child)
804 {
805 	int res = False;
806 
807 #ifdef SHAPE
808 	if (parent && child) {
809 		int child_x = 0;
810 		int child_y = 0;
811 		unsigned int width, height, bw;
812 
813 		if (parent->shape == NULL)
814 			return False;
815 #ifndef STRICT_GEOMETRY
816 		get_current_canvas_geometry (child, &child_x, &child_y, &width,
817 																 &height, &bw);
818 #else
819 		child_x = child->root_x - parent->root_x;
820 		child_y = child->root_y - parent->root_y;
821 		width = child->width;
822 		height = child->height;
823 		bw = child->bw;
824 #endif
825 		res =
826 				combine_canvas_shape_at_geom (parent, child, child_x, child_y,
827 																			width, height, bw);
828 	}
829 #endif
830 	return res;
831 }
832 
833 
834 Bool
combine_canvas_shape_at(ASCanvas * parent,ASCanvas * child,int child_x,int child_y)835 combine_canvas_shape_at (ASCanvas * parent, ASCanvas * child, int child_x,
836 												 int child_y)
837 {
838 #ifdef SHAPE
839 	if (child) {
840 		unsigned int width, height, bw;
841 
842 #ifndef STRICT_GEOMETRY
843 		get_current_canvas_geometry (child, NULL, NULL, &width, &height, &bw);
844 #else
845 		width = child->width;
846 		height = child->height;
847 		bw = child->bw;
848 #endif
849 
850 		return combine_canvas_shape_at_geom (parent, child, child_x, child_y,
851 																				 width, height, bw);
852 	}
853 #endif
854 	return False;
855 }
856 
is_canvas_needs_redraw(ASCanvas * pc)857 Bool is_canvas_needs_redraw (ASCanvas * pc)
858 {
859 	return pc ? get_flags (pc->state, CANVAS_DIRTY) : False;
860 }
861 
is_canvas_dirty(ASCanvas * pc)862 Bool is_canvas_dirty (ASCanvas * pc)
863 {
864 	return pc ? get_flags (pc->state,
865 												 CANVAS_DIRTY | CANVAS_OUT_OF_SYNC |
866 												 CANVAS_MASK_OUT_OF_SYNC) : False;
867 }
868 
869 
870 ASFlagType
configure_canvas(ASCanvas * pc,int x,int y,unsigned int width,unsigned int height,ASFlagType mask)871 configure_canvas (ASCanvas * pc, int x, int y, unsigned int width,
872 									unsigned int height, ASFlagType mask)
873 {
874 	ASFlagType changes = 0;
875 	XWindowChanges xwc;
876 	int curr_x, curr_y;
877 	unsigned int curr_width, curr_height, curr_bw;
878 
879 	if (pc == NULL)
880 		return 0;
881 
882 	get_current_canvas_geometry (pc, &curr_x, &curr_y, &curr_width,
883 															 &curr_height, &curr_bw);
884 
885 	LOCAL_DEBUG_CALLER_OUT ("canvas(%p)->window(%lx)->geom(%ux%u%+d%+d)", pc,
886 													pc->w, width, height, x, y);
887 	/* Setting background to None to avoid background pixmap tiling
888 	 * while resizing */
889 	if (!get_flags (mask, CWWidth))
890 		width = pc->width;
891 	else if (width > MAX_POSITION) {
892 #ifdef DEBUG_ALLOCS
893 		AS_ASSERT (0);
894 #endif
895 		width = pc->width;
896 	} else if (AS_ASSERT (width))
897 		width = 1;
898 
899 	if (!get_flags (mask, CWHeight))
900 		height = pc->height;
901 	else if (height > MAX_POSITION) {
902 #ifdef DEBUG_ALLOCS
903 		AS_ASSERT (0);
904 #endif
905 		height = pc->height;
906 	} else if (AS_ASSERT (height))
907 		height = 1;
908 
909 	if (get_flags (mask, CWX) && curr_x != x)
910 		set_flags (changes, CANVAS_X_CHANGED);
911 	if (get_flags (mask, CWY) && curr_y != y)
912 		set_flags (changes, CANVAS_Y_CHANGED);
913 	if (width != curr_width)
914 		set_flags (changes, CANVAS_WIDTH_CHANGED);
915 	if (height != curr_height)
916 		set_flags (changes, CANVAS_HEIGHT_CHANGED);
917 
918 	if (changes == 0)
919 		return 0;
920 	xwc.x = x;
921 	xwc.y = y;
922 	xwc.width = width;
923 	xwc.height = height;
924 
925 	if ((pc->width < width || pc->height < height)
926 			&& !get_flags (pc->state, CANVAS_CONTAINER))
927 		XSetWindowBackgroundPixmap (dpy, pc->w, None);
928 
929 	LOCAL_DEBUG_OUT ("XConfigureWindow( %lX, %lX, %dx%d%+d%+d );", pc->w,
930 									 mask, width, height, x, y);
931 	XConfigureWindow (dpy, pc->w, mask, &xwc);
932 /*fprintf( stderr, "client_changes: ( %lX, %lX, %dx%d%+d%+d ); was %dx%d%+d%+d\n", pc->w, mask, width, height, x, y, curr_width, curr_height, curr_x, curr_y );*/
933 	return changes;
934 }
935 
936 ASFlagType
resize_canvas(ASCanvas * pc,unsigned int width,unsigned int height)937 resize_canvas (ASCanvas * pc, unsigned int width, unsigned int height)
938 {
939 	if (pc == NULL)
940 		return 0;
941 
942 	return configure_canvas (pc, 0, 0, width, height, CWWidth | CWHeight);
943 }
944 
945 
946 
947 ASFlagType
moveresize_canvas(ASCanvas * pc,int x,int y,unsigned int width,unsigned int height)948 moveresize_canvas (ASCanvas * pc, int x, int y, unsigned int width,
949 									 unsigned int height)
950 {
951 	if (pc == NULL)
952 		return 0;
953 	return configure_canvas (pc, x, y, width, height,
954 													 CWX | CWY | CWWidth | CWHeight);
955 }
956 
957 
move_canvas(ASCanvas * pc,int x,int y)958 void move_canvas (ASCanvas * pc, int x, int y)
959 {
960 	if (pc == NULL)
961 		return;
962 
963 	LOCAL_DEBUG_CALLER_OUT ("canvas(%p)->window(%lx)->geom(%+d%+d)", pc,
964 													pc->w, x, y);
965 	XMoveWindow (dpy, pc->w, x, y);
966 }
967 
968 
unmap_canvas_window(ASCanvas * pc)969 void unmap_canvas_window (ASCanvas * pc)
970 {
971 	if (pc && pc->w != None) {
972 		XUnmapWindow (dpy, pc->w);
973 		clear_flags (pc->state, CANVAS_MAPPED);
974 	}
975 }
976 
map_canvas_window(ASCanvas * pc,Bool raised)977 void map_canvas_window (ASCanvas * pc, Bool raised)
978 {
979 	LOCAL_DEBUG_CALLER_OUT ("pc=%p", pc);
980 	if (pc == NULL)
981 		return;
982 
983 	LOCAL_DEBUG_OUT ("raised = %s", raised ? "True" : "False");
984 	if (pc->w != None) {
985 		if (raised)
986 			XMapRaised (dpy, pc->w);
987 		else
988 			XMapWindow (dpy, pc->w);
989 		set_flags (pc->state, CANVAS_MAPPED);
990 	}
991 }
992 
993 void
quietly_reparent_canvas(ASCanvas * pc,Window dst,long event_mask,Bool use_root_pos,Window below)994 quietly_reparent_canvas (ASCanvas * pc, Window dst, long event_mask,
995 												 Bool use_root_pos, Window below)
996 {
997 	if (pc) {
998 		int x = 0, y = 0;
999 		unsigned int bw = pc->bw;
1000 		Window parent = None;
1001 
1002 		if (dst == None)
1003 			dst = ASDefaultRoot;
1004 
1005 		if (use_root_pos) {
1006 			x = pc->root_x;
1007 			y = pc->root_y;
1008 		} else
1009 			get_canvas_position (pc, &parent, &x, &y, &bw);
1010 
1011 		if (parent != dst) {
1012 			/* blocking UnmapNotify events since that may bring us into Withdrawn state */
1013 			XSelectInput (dpy, pc->w, event_mask & ~StructureNotifyMask);
1014 			LOCAL_DEBUG_OUT ("XReparentWindow( %lX, %lX, %+d%+d ), bw = %d",
1015 											 pc->w, dst, x, y, bw);
1016 			if (below != None && get_flags (pc->state, CANVAS_MAPPED)) {
1017 				Window w[2];
1018 
1019 				XUnmapWindow (dpy, pc->w);
1020 				XReparentWindow (dpy, pc->w, (dst != None) ? dst : ASDefaultRoot,
1021 												 x, y);
1022 				w[0] = below;
1023 				w[1] = pc->w;
1024 				XRestackWindows (dpy, w, 2);
1025 				XMapWindow (dpy, pc->w);
1026 			} else
1027 				XReparentWindow (dpy, pc->w, (dst != None) ? dst : ASDefaultRoot,
1028 												 x, y);
1029 			XSelectInput (dpy, pc->w, event_mask);
1030 		}
1031 	}
1032 }
1033 
reparent_canvas_window(ASCanvas * pc,Window dst,int x,int y)1034 void reparent_canvas_window (ASCanvas * pc, Window dst, int x, int y)
1035 {
1036 	if (pc) {
1037 		if (dst == None)
1038 			dst = ASDefaultRoot;
1039 		LOCAL_DEBUG_OUT ("XReparentWindow( %lX, %lX, +0+0 )", pc->w, dst);
1040 		XReparentWindow (dpy, pc->w, dst, 0, 0);
1041 	}
1042 }
1043 
1044 void
add_canvas_grid(ASGrid * grid,ASCanvas * canvas,int outer_gravity,int inner_gravity,Bool absolute)1045 add_canvas_grid (ASGrid * grid, ASCanvas * canvas, int outer_gravity,
1046 								 int inner_gravity, Bool absolute)
1047 {
1048 	if (canvas && grid) {
1049 		unsigned long flags = absolute ? ASGL_Absolute : 0;
1050 		int x = canvas->root_x;
1051 		int y = canvas->root_y;
1052 
1053 		if (!absolute) {
1054 			x += grid->curr_vx;
1055 			y += grid->curr_vy;
1056 		}
1057 
1058 		LOCAL_DEBUG_CALLER_OUT ("(%p,%ux%u%+d%+d)", canvas, canvas->width,
1059 														canvas->height, canvas->root_x,
1060 														canvas->root_y);
1061 		add_gridline (grid, y, x, x + canvas->width + canvas->bw * 2,
1062 									outer_gravity, inner_gravity, flags);
1063 		add_gridline (grid, y + canvas->height + canvas->bw * 2, x,
1064 									x + canvas->width + canvas->bw * 2, inner_gravity,
1065 									outer_gravity, flags);
1066 		set_flags (flags, ASGL_Vertical);
1067 		add_gridline (grid, x, y, y + canvas->height + canvas->bw * 2,
1068 									outer_gravity, inner_gravity, flags);
1069 		add_gridline (grid, x + canvas->width + canvas->bw * 2, y,
1070 									y + canvas->height + canvas->bw * 2, inner_gravity,
1071 									outer_gravity, flags);
1072 	}
1073 }
1074 
set_root_clip_area(ASCanvas * canvas)1075 void set_root_clip_area (ASCanvas * canvas)
1076 {
1077 	if (canvas) {
1078 		ASDefaultScr->RootClipArea.x = canvas->root_x + (int)canvas->bw;
1079 		ASDefaultScr->RootClipArea.y = canvas->root_y + (int)canvas->bw;
1080 		ASDefaultScr->RootClipArea.width = canvas->width;
1081 		ASDefaultScr->RootClipArea.height = canvas->height;
1082 		if (ASDefaultScr->RootImage) {
1083 			safe_asimage_destroy (ASDefaultScr->RootImage);
1084 			ASDefaultScr->RootImage = NULL;
1085 		}
1086 	}
1087 }
1088 
1089 /*************************************************************************/
1090 
send_canvas_configure_notify(ASCanvas * parent,ASCanvas * canvas)1091 void send_canvas_configure_notify (ASCanvas * parent, ASCanvas * canvas)
1092 {
1093 	LOCAL_DEBUG_CALLER_OUT ("%p,%p", parent, canvas);
1094 	if (canvas) {
1095 		XEvent client_event;
1096 		unsigned int uwidth, uheight, ubw;
1097 
1098 		client_event.type = ConfigureNotify;
1099 		client_event.xconfigure.display = dpy;
1100 		client_event.xconfigure.event = canvas->w;
1101 		client_event.xconfigure.window = canvas->w;
1102 
1103 		get_current_canvas_geometry (canvas, &(client_event.xconfigure.x),
1104 																 &(client_event.xconfigure.y), &uwidth,
1105 																 &uheight, &ubw);
1106 		client_event.xconfigure.width = uwidth;
1107 		client_event.xconfigure.height = uheight;
1108 		client_event.xconfigure.border_width = ubw;
1109 
1110 		if (parent) {
1111 #if 1
1112 			Window wdumm;
1113 
1114 			XTranslateCoordinates (dpy, canvas->w, ASDefaultRoot, 0, 0,
1115 														 &(client_event.xconfigure.x),
1116 														 &(client_event.xconfigure.y), &wdumm);
1117 #else														/* we maybe called before parent's geometry is updated - so don't trust it! */
1118 			client_event.xconfigure.x += parent->root_x + (int)parent->bw;
1119 			client_event.xconfigure.y += parent->root_y + (int)parent->bw;
1120 #endif
1121 			/* Real ConfigureNotify events say we're above title window, so ... */
1122 			/* what if we don't have a title ????? */
1123 			client_event.xconfigure.above = parent->w;
1124 		} else
1125 			client_event.xconfigure.above = ASDefaultRoot;
1126 		client_event.xconfigure.override_redirect = False;
1127 		LOCAL_DEBUG_OUT ("geom= %dx%d%+d%+d", client_event.xconfigure.width,
1128 										 client_event.xconfigure.height,
1129 										 client_event.xconfigure.x, client_event.xconfigure.y);
1130 		XSendEvent (dpy, canvas->w, False, StructureNotifyMask, &client_event);
1131 	}
1132 }
1133