]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/services/gfx/mw/v2_0/src/nanox/srvutil.c
Initial revision
[karo-tx-redboot.git] / packages / services / gfx / mw / v2_0 / src / nanox / srvutil.c
1 /*
2  * Copyright (c) 2000 Greg Haerr <greg@censoft.com>
3  * Copyright (c) 1991 David I. Bell
4  * Permission is granted to use, distribute, or modify this source,
5  * provided that this copyright notice remains intact.
6  *
7  * Graphics server utility routines for windows.
8  */
9 #include <stdio.h>
10 #include <stdlib.h>
11
12 #ifndef __PACIFIC__
13 #include <fcntl.h>
14 #endif
15
16 #include "serv.h"
17
18 /*
19  * Help prevent future bugs by defining this variable to an illegal value.
20  * These routines should not be referencing this, but should be using
21  * unmapcount instead.
22  */
23 #define mapped  cannotusemapped
24
25 /*
26  * Redraw the screen completely.
27  */
28 void
29 GsRedrawScreen(void)
30 {
31         /* Redraw all windows*/
32         GsExposeArea(rootwp, 0, 0, rootwp->width, rootwp->height, NULL);
33 }
34
35 /*
36  * Activate Screen Saver.
37  */
38 void
39 GsActivateScreenSaver(void *arg)
40 {
41         screensaver_active = GR_TRUE;
42         GsDeliverScreenSaverEvent(GR_TRUE);
43 }
44
45 /*
46  * Deactivate screen saver and reset timer if active.
47  */
48 void
49 GsResetScreenSaver(void)
50 {
51         MWTIMER *timer;
52
53         if(screensaver_active == GR_TRUE) {
54                 screensaver_active = GR_FALSE;
55                 GsDeliverScreenSaverEvent(GR_FALSE);
56         }
57         if(screensaver_delay) {
58                 if((timer = GdFindTimer(GsActivateScreenSaver)))
59                         GdDestroyTimer(timer);
60                 GdAddTimer(screensaver_delay, GsActivateScreenSaver,
61                         GsActivateScreenSaver);
62         }
63 }
64
65 void
66 GsTimerCB (void *arg) 
67 {
68     GR_TIMER *timer = (GR_TIMER*) arg;
69
70     GsDeliverTimerEvent (timer->owner, timer->wid, timer->id);
71 }
72
73
74 /*
75  * Unmap the window to make it and its children invisible on the screen.
76  * This is a recursive routine which increments the unmapcount values for
77  * this window and all of its children, and causes exposure events for
78  * windows which are newly uncovered.
79  * If temp_unmap set, don't reset focus or generate mouse/focus events,
80  * as window will be mapped again momentarily (window move, resize, etc)
81  */
82 void GsWpUnmapWindow(GR_WINDOW *wp, GR_BOOL temp_unmap)
83 {
84         GR_WINDOW       *pwp;           /* parent window */
85         GR_WINDOW       *sibwp;         /* sibling window */
86         GR_WINDOW       *childwp;       /* child window */
87         GR_SIZE         bs;             /* border size of this window */
88
89         if (wp == rootwp) {
90                 GsError(GR_ERROR_ILLEGAL_ON_ROOT_WINDOW, wp->id);
91                 return;
92         }
93
94         if (wp == clipwp)
95                 clipwp = NULL;
96
97         ++wp->unmapcount;
98
99         for (childwp = wp->children; childwp; childwp = childwp->siblings)
100                 GsWpUnmapWindow(childwp, temp_unmap);
101
102         if (!temp_unmap && wp == mousewp) {
103                 GsCheckMouseWindow();
104                 GsCheckCursor();
105         }
106
107         if (!temp_unmap && wp == focuswp) {
108                 if (focusfixed)
109                         /* don't revert to mouse enter/leave focus if fixed*/
110                         focuswp = rootwp;
111                 else {
112                         focusfixed = GR_FALSE;
113                         GsCheckFocusWindow();
114                 }
115         }
116
117         /* Send update event if just unmapped*/
118         if (wp->unmapcount == 1) {
119                 GsDeliverUpdateEvent(wp, 
120                         (temp_unmap? GR_UPDATE_UNMAPTEMP: GR_UPDATE_UNMAP),
121                         0, 0, 0, 0);
122         }
123
124         /*
125          * If this is an input-only window or the parent window is
126          * still unmapped, then we are all done.
127          */
128         if (!wp->output || wp->parent->unmapcount)
129                 return;
130
131         /*
132          * Clear the area in the parent for this window, causing an
133          * exposure event for it.  Take into account the border size.
134          */
135         bs = wp->bordersize;
136         pwp = wp->parent;
137         GsWpClearWindow(pwp, wp->x - pwp->x - bs, wp->y - pwp->y - bs,
138                 wp->width + bs * 2, wp->height + bs * 2, GR_TRUE);
139
140         /*
141          * Finally clear and redraw all parts of our lower sibling
142          * windows that were covered by this window.
143          */
144         sibwp = wp;
145         while (sibwp->siblings) {
146                 sibwp = sibwp->siblings;
147                 GsExposeArea(sibwp, wp->x - bs, wp->y - bs,
148                         wp->width + bs * 2, wp->height + bs * 2, NULL);
149         }
150 }
151
152 /*
153  * Map the window to possibly make it and its children visible on the screen.
154  * This is a recursive routine which decrements the unmapcount values for
155  * this window and all of its children, and causes exposure events for
156  * those windows which become visible.
157  * If temp is set, then window is being mapped again after a temporary
158  * unmap, so don't reset focus or generate mouse/focus events.
159  */
160 void GsWpMapWindow(GR_WINDOW *wp, GR_BOOL temp)
161 {
162         if (wp == rootwp) {
163                 GsError(GR_ERROR_ILLEGAL_ON_ROOT_WINDOW, wp->id);
164                 return;
165         }
166
167         if (wp->unmapcount)
168                 --wp->unmapcount;
169
170         if (!temp && wp->unmapcount == 0) {
171                 GsCheckMouseWindow();
172                 GsCheckFocusWindow();
173                 GsCheckCursor();
174         }
175
176         /* send update event if just mapped*/
177         if (wp->unmapcount == 0) {
178                 GsDeliverUpdateEvent(wp, GR_UPDATE_MAP, wp->x, wp->y,
179                         wp->width, wp->height);
180         }
181
182         /*
183          * If the window is an output window and just became visible,
184          * then draw its border, clear it to the background color, and
185          * generate an exposure event.
186          */
187         if (wp->output && (wp->unmapcount == 0)) {
188                 GsDrawBorder(wp);
189                 GsWpClearWindow(wp, 0, 0, wp->width, wp->height, GR_TRUE);
190         }
191
192         /*
193          * Do the same thing for the children.
194          */
195         for (wp = wp->children; wp; wp = wp->siblings)
196                 GsWpMapWindow(wp, temp);
197 }
198
199 /*
200  * Destroy the specified window, and all of its children.
201  * This is a recursive routine.
202  */
203 void GsWpDestroyWindow(GR_WINDOW *wp)
204 {
205         GR_WINDOW       *prevwp;        /* previous window pointer */
206         GR_EVENT_CLIENT *ecp;           /* selections for window */
207         GR_WINDOW_ID    oldwid;         /* old selection owner */
208
209         if (wp == rootwp) {
210                 GsError(GR_ERROR_ILLEGAL_ON_ROOT_WINDOW, wp->id);
211                 return;
212         }
213
214         /* Disable selection if this window is the owner */
215         if(selection_owner.wid == wp->id) {
216                 oldwid = selection_owner.wid;
217                 selection_owner.wid = 0;
218                 if(selection_owner.typelist) free(selection_owner.typelist);
219                 GsDeliverSelectionChangedEvent(oldwid, 0);
220         }
221
222         /*
223          * Unmap the window first.
224          */
225         if (wp->unmapcount == 0)
226                 GsWpUnmapWindow(wp, GR_FALSE);
227
228         /* send update event*/
229         GsDeliverUpdateEvent(wp, GR_UPDATE_DESTROY, wp->x, wp->y,
230                 wp->width, wp->height);
231
232         /*
233          * Destroy all children.
234          */
235         while (wp->children)
236                 GsWpDestroyWindow(wp->children);
237
238         /*
239          * Free all client selection structures.
240          */
241         while (wp->eventclients) {
242                 ecp = wp->eventclients;
243                 wp->eventclients = ecp->next;
244                 free(ecp);
245         }
246
247         /*
248          * Remove this window from the child list of its parent.
249          */
250         prevwp = wp->parent->children;
251         if (prevwp == wp)
252                 wp->parent->children = wp->siblings;
253         else {
254                 while (prevwp->siblings != wp)
255                         prevwp = prevwp->siblings;
256                 prevwp->siblings = wp->siblings;
257         }
258         wp->siblings = NULL;
259
260         /*
261          * Remove this window from the complete list of windows.
262          */
263         prevwp = listwp;
264         if (prevwp == wp)
265                 listwp = wp->next;
266         else {
267                 while (prevwp->next != wp)
268                         prevwp = prevwp->next;
269                 prevwp->next = wp->next;
270         }
271         wp->next = NULL;
272
273         /*
274          * Forget various information if they related to this window.
275          * Then finally free the structure.
276          */
277         if (wp == clipwp)
278                 clipwp = NULL;
279         if (wp == grabbuttonwp)
280                 grabbuttonwp = NULL;
281         if (wp == cachewp) {
282                 cachewindowid = 0;
283                 cachewp = NULL;
284         }
285         if (wp == focuswp) {
286                 /* don't revert to mouse enter/leave focus if fixed*/
287                 /*focusfixed = GR_FALSE;*/
288                 focuswp = rootwp;
289         }
290
291         GsCheckMouseWindow();
292
293         if(wp->title)
294                 free(wp->title);
295
296         free(wp);
297 }
298
299 /*
300  * Draw a window's background pixmap.
301  *
302  * The flags mean:
303  *   GR_BACKGROUND_TILE- tile the pixmap across the window (default).
304  *   GR_BACKGROUND_TOPLEFT- draw the pixmap at (0,0) relative to the window.
305  *   GR_BACKGROUND_CENTER- draw the pixmap in the middle of the window.
306  *   GR_BACKGROUND_STRETCH- stretch the pixmap within the window.
307  *   GR_BACKGROUND_TRANS- if the pixmap is smaller than the window and not
308  *     using tile mode, there will be gaps around the pixmap. This flag causes
309  *     to not fill in the spaces with the background colour.
310  */
311 void GsWpDrawBackgroundPixmap(GR_WINDOW *wp, GR_PIXMAP *pm, GR_COORD x,
312         GR_COORD y, GR_SIZE width, GR_SIZE height)
313 {
314         GR_SIZE destwidth, destheight, fillwidth, fillheight, pmwidth, pmheight;
315         GR_COORD fromx, fromy, destx, desty, pixmapx = 0, pixmapy = 0;
316
317         if(wp->bgpixmapflags & (GR_BACKGROUND_TOPLEFT|GR_BACKGROUND_STRETCH)) {
318                 pixmapx = 0;
319                 pixmapy = 0;
320         } else if(wp->bgpixmapflags & GR_BACKGROUND_CENTER) {
321                 if(pm->width >= wp->width) pixmapx = 0;
322                 else pixmapx = (wp->width - pm->width) / 2;
323                 if(pm->height >= wp->height) pixmapy = 0;
324                 else pixmapy = (wp->height - pm->height) / 2;
325         } else { 
326                 /* GR_BACKGROUND_TILE (default)*/
327                 GsWpTileBackgroundPixmap(wp, pm, x, y, width, height);
328                 return;
329         }
330
331         if(pm->width > wp->width) pmwidth = wp->width;
332         else pmwidth = pm->width;
333         if(pm->height > wp->height) pmheight = wp->height;
334         else pmheight = pm->height;
335
336         if(x > pixmapx) {
337                 destx = x;
338                 fromx = x - pixmapx;
339                 destwidth = pixmapx + pmwidth - x;
340         } else {
341                 destx = pixmapx;
342                 fromx = 0;
343                 destwidth = x + width - pixmapx;
344         }
345
346         if(y > pixmapy) {
347                 desty = y;
348                 fromy = y - pixmapy;
349                 destheight = pixmapy + pmheight - desty;
350         } else {
351                 desty = pixmapy;
352                 fromy = 0;
353                 destheight = y + height - pixmapy;
354         }
355
356         if(destwidth > 0 && destheight > 0) {
357                 if (wp->bgpixmapflags & GR_BACKGROUND_STRETCH) {
358                         GdStretchBlit(wp->psd, destx + wp->x, desty + wp->y,
359                                 destwidth, destheight, pm->psd, fromx, fromy,
360                                 pm->width, pm->height, MWROP_COPY);
361                 } else GdBlit(wp->psd, destx + wp->x, desty + wp->y, destwidth,
362                         destheight, pm->psd, fromx, fromy, MWROP_COPY);
363         }
364
365         if(wp->bgpixmapflags & (GR_BACKGROUND_TRANS|GR_BACKGROUND_STRETCH))
366                 return;
367
368         /* Fill in the gaps around the pixmap */
369         if(x < pixmapx) {
370                 fillwidth = pixmapx - x;
371                 if(fillwidth > width) fillwidth = width;
372                 fillheight = height;
373                 GdFillRect(wp->psd, wp->x + x, wp->y + y, fillwidth,fillheight);
374         }
375         if((x + width) > (pixmapx + pmwidth)) {
376                 fillwidth = (x + width) - (pixmapx + pmwidth);
377                 if(fillwidth > width) fillwidth = width;
378                 fillheight = height;
379                 if(x < (pixmapx + pmwidth)) destx = pixmapx + pmwidth + wp->x;
380                 else destx = x + wp->x;
381                 GdFillRect(wp->psd, destx, wp->y + y, fillwidth, fillheight);
382         }
383         if(y < pixmapy) {
384                 fillheight = pixmapy - y;
385                 if(fillheight > height) fillheight = height;
386                 if(x < pixmapx) destx = pixmapx + wp->x;
387                 else destx = x + wp->x;
388                 if((x + width) > (pixmapx + pmwidth))
389                         fillwidth = pixmapx + pmwidth - destx;
390                 else fillwidth = x + width - destx;
391                 if((fillwidth > 0) && (fillheight > 0)) {
392                         GdFillRect(wp->psd, destx, wp->y + y, fillwidth,
393                                                         fillheight);
394                 }
395         }
396         if((y + height) > (pixmapy + pmheight)) {
397                 fillheight = (y + height) - (pixmapy + pmheight);
398                 if(fillheight > height) fillheight = height;
399                 if(x < pixmapx) destx = pixmapx + wp->x;
400                 else destx = x + wp->x;
401                 if(y < (pixmapy + pmheight)) desty = pixmapy + pmheight + wp->y;
402                 else desty = y + wp->y;
403         
404                 if((x + width) > (pixmapx + pmwidth))
405                         fillwidth = pixmapx + pmwidth - destx;
406                 else fillwidth = x + width - destx;
407                 if((fillwidth > 0) && (fillheight > 0)) {
408                         GdFillRect(wp->psd, destx, desty, fillwidth,fillheight);
409                 }
410         }
411 }
412
413 /*
414  * Draw a tiled pixmap window background.
415  */
416 void 
417 GsWpTileBackgroundPixmap(GR_WINDOW *wp, GR_PIXMAP *pm, GR_COORD x, GR_COORD y,
418         GR_SIZE width, GR_SIZE height)
419 {
420         GR_COORD tilex = 0, tiley = 0, fromx, fromy, cx, cy;
421         GR_SIZE destwidth, destheight, pmwidth, pmheight, cwidth, cheight;
422
423         if(pm->width > wp->width) pmwidth = wp->width;
424         else pmwidth = pm->width;
425         if(pm->height > wp->height) pmheight = wp->height;
426         else pmheight = pm->height;
427
428         for(;tiley < wp->height; tiley += pmheight, tilex = 0) {
429                 if(tiley > (y + height)) continue;
430                 if(y > (tiley + pmheight)) continue;
431                 if((tiley + pmheight) > wp->height)
432                         destheight = wp->height - tiley;
433                 else destheight = pmheight;
434                 for(;tilex < wp->width; tilex += pmwidth) {
435                         if(tilex > (x + width)) continue;
436                         if(x > (tilex + pmwidth)) continue;
437                         if((tilex + pmwidth) > wp->width)
438                                 destwidth = wp->width - tilex;
439                         else destwidth = pmwidth;
440
441                         if((tilex >= x) && ((tilex + destwidth)<=(x + width))) {
442                                 fromx = 0;
443                                 cx = tilex + wp->x;
444                                 cwidth = destwidth;
445                         } else {
446                                 if(x > tilex) {
447                                         fromx = x - tilex;
448                                         cwidth = destwidth - fromx;
449                                 } else {
450                                         fromx = 0;
451                                         cwidth = x + width - tilex;
452                                 }
453                                 if(cwidth > width) cwidth = width;
454                                 if(cwidth > destwidth) cwidth = destwidth;
455                                 cx = wp->x + tilex + fromx;
456                         }
457
458                         if((tiley >= y)&&((tiley + destheight)<=(y + height))) {
459                                 fromy = 0;
460                                 cy = tiley + wp->y;
461                                 cheight = destheight;
462                         } else {
463                                 if(y > tiley) {
464                                         fromy = y - tiley;
465                                         cheight = destheight - fromy;
466                                 } else {
467                                         fromy = 0;
468                                         cheight = y + height - tiley;
469                                 }
470                                 if(cwidth > width) cwidth = width;
471                                 if(cheight > destheight) cheight = destheight;
472                                 cy = wp->y + tiley + fromy;
473                         }
474
475                         if((cwidth > 0) && (cheight > 0)) {
476                                 GdBlit(wp->psd, cx, cy, cwidth, cheight,
477                                         pm->psd, fromx, fromy, MWROP_COPY);
478                         }
479                 }
480         }
481 }
482
483 /*
484  * Clear the specified area of a window and possibly make an exposure event.
485  * This sets the area window to its background color or pixmap.  If the
486  * exposeflag is nonzero, then this also creates an exposure event for the
487  * window.
488  */
489 void
490 GsWpClearWindow(GR_WINDOW *wp, GR_COORD x, GR_COORD y, GR_SIZE width,
491         GR_SIZE  height, GR_BOOL exposeflag)
492 {
493         if (wp->unmapcount || !wp->output)
494                 return;
495         /*
496          * Reduce the arguments so that they actually lie within the window.
497          */
498         if (x < 0) {
499                 width += x;
500                 x = 0;
501         }
502         if (y < 0) {
503                 height += y;
504                 y = 0;
505         }
506         if (x + width > wp->width)
507                 width = wp->width - x;
508         if (y + height > wp->height)
509                 height = wp->height - y;
510
511         /*
512          * Now see if the region is really in the window.  If not, then
513          * do nothing.
514          */
515         if ((x >= wp->width) || (y >= wp->height) || (width <= 0) ||
516                 (height <= 0))
517                         return;
518
519         /*
520          * Draw the background of the window.
521          * Invalidate the current graphics context since
522          * we are changing the foreground color and mode.
523          */
524         GsSetClipWindow(wp, NULL, 0);
525         curgcp = NULL;
526         if (!(wp->props & GR_WM_PROPS_NOBACKGROUND)) {
527                 GdSetMode(GR_MODE_COPY);
528                 GdSetForeground(GdFindColor(wp->background));
529                 if (wp->bgpixmap) {
530                         GsWpDrawBackgroundPixmap(wp, wp->bgpixmap, x, y,
531                                 width, height);
532                 } else {
533                         GdFillRect(wp->psd, wp->x + x, wp->y + y, width,height);
534                 }
535         }
536
537         /*
538          * Now do the exposure if required.
539          */
540         if (exposeflag)
541                 GsDeliverExposureEvent(wp, x, y, width, height);
542 }
543
544 /*
545  * Handle the exposing of the specified absolute region of the screen,
546  * starting with the specified window.  That window and all of its
547  * children will be redrawn and/or exposure events generated if they
548  * overlap the specified area.  This is a recursive routine.
549  */
550 void
551 GsExposeArea(GR_WINDOW *wp, GR_COORD rootx, GR_COORD rooty, GR_SIZE width,
552         GR_SIZE height, GR_WINDOW *stopwp)
553 {
554         if (!wp->output || wp->unmapcount || wp == stopwp)
555                 return;
556
557         /*
558          * First see if the area overlaps the window including the border.
559          * If not, then there is nothing more to do.
560          */
561         if ((rootx >= wp->x + wp->width + wp->bordersize) ||
562                 (rooty >= wp->y + wp->height + wp->bordersize) ||
563                 (rootx + width <= wp->x - wp->bordersize) ||
564                 (rooty + height <= wp->y - wp->bordersize))
565                         return;
566
567         /*
568          * The area does overlap the window.  See if the area overlaps
569          * the border, and if so, then redraw it.
570          */
571         if ((rootx < wp->x) || (rooty < wp->y) ||
572                 (rootx + width > wp->x + wp->width) ||
573                 (rooty + height > wp->y + wp->height))
574                         GsDrawBorder(wp);
575
576         /*
577          * Now clear the window itself in the specified area,
578          * which might cause an exposure event.
579          */
580         GsWpClearWindow(wp, rootx - wp->x, rooty - wp->y,
581                 width, height, GR_TRUE);
582
583         /*
584          * Now do the same for all the children.
585          */
586         for (wp = wp->children; wp; wp = wp->siblings)
587                 GsExposeArea(wp, rootx, rooty, width, height, stopwp);
588 }
589
590 /*
591  * Draw the border of a window if there is one.
592  * Note: To allow the border to be drawn with the correct clipping,
593  * we temporarily grow the size of the window to include the border.
594  */
595 void GsDrawBorder(GR_WINDOW *wp)
596 {
597         GR_COORD        lminx;          /* left edge minimum x */
598         GR_COORD        rminx;          /* right edge minimum x */
599         GR_COORD        tminy;          /* top edge minimum y */
600         GR_COORD        bminy;          /* bottom edge minimum y */
601         GR_COORD        topy;           /* top y value of window */
602         GR_COORD        boty;           /* bottom y value of window */
603         GR_SIZE         width;          /* original width of window */
604         GR_SIZE         height;         /* original height of window */
605         GR_SIZE         bs;             /* border size */
606
607         bs = wp->bordersize;
608         if (bs <= 0)
609                 return;
610
611         width = wp->width;
612         height = wp->height;
613         lminx = wp->x - bs;
614         rminx = wp->x + width;
615         tminy = wp->y - bs;
616         bminy = wp->y + height;
617         topy = wp->y;
618         boty = bminy - 1;
619  
620         wp->x -= bs;
621         wp->y -= bs;
622         wp->width += (bs * 2);
623         wp->height += (bs * 2);
624         wp->bordersize = 0;
625
626         clipwp = NULL;
627         GsSetClipWindow(wp, NULL, 0);
628         curgcp = NULL;
629         GdSetMode(GR_MODE_COPY);
630         GdSetForeground(GdFindColor(wp->bordercolor));
631
632         if (bs == 1) {
633                 GdLine(wp->psd, lminx, tminy, rminx, tminy, TRUE);
634                 GdLine(wp->psd, lminx, bminy, rminx, bminy, TRUE);
635                 GdLine(wp->psd, lminx, topy, lminx, boty, TRUE);
636                 GdLine(wp->psd, rminx, topy, rminx, boty, TRUE);
637         } else {
638                 GdFillRect(wp->psd, lminx, tminy, width + bs * 2, bs);
639                 GdFillRect(wp->psd, lminx, bminy, width + bs * 2, bs);
640                 GdFillRect(wp->psd, lminx, topy, bs, height);
641                 GdFillRect(wp->psd, rminx, topy, bs, height);
642         }
643
644         /*
645          * Restore the true window size.
646          * Forget the currently clipped window since we messed it up.
647          */
648         wp->x += bs;
649         wp->y += bs;
650         wp->width -= (bs * 2);
651         wp->height -= (bs * 2);
652         wp->bordersize = bs;
653         clipwp = NULL;
654 }
655
656 /*
657  * Check to see if the first window overlaps the second window.
658  */
659 GR_BOOL GsCheckOverlap(GR_WINDOW *topwp, GR_WINDOW *botwp)
660 {
661         GR_COORD        minx1;
662         GR_COORD        miny1;
663         GR_COORD        maxx1;
664         GR_COORD        maxy1;
665         GR_COORD        minx2;
666         GR_COORD        miny2;
667         GR_COORD        maxx2;
668         GR_COORD        maxy2;
669         GR_SIZE         bs;
670
671         if (!topwp->output || topwp->unmapcount || botwp->unmapcount)
672                 return GR_FALSE;
673
674         bs = topwp->bordersize;
675         minx1 = topwp->x - bs;
676         miny1 = topwp->y - bs;
677         maxx1 = topwp->x + topwp->width + bs - 1;
678         maxy1 = topwp->y + topwp->height + bs - 1;
679
680         bs = botwp->bordersize;
681         minx2 = botwp->x - bs;
682         miny2 = botwp->y - bs;
683         maxx2 = botwp->x + botwp->width + bs - 1;
684         maxy2 = botwp->y + botwp->height + bs - 1;
685
686         if ((minx1 > maxx2) || (minx2 > maxx1) || (miny1 > maxy2)
687                 || (miny2 > maxy1))
688                         return GR_FALSE;
689
690         return GR_TRUE;
691 }
692
693 /*
694  * Return a pointer to the window structure with the specified window id.
695  * Returns NULL if the window does not exist.
696  */
697 GR_WINDOW *
698 GsFindWindow(GR_WINDOW_ID id)
699 {
700         GR_WINDOW       *wp;            /* current window pointer */
701
702         /*
703          * See if this is the root window or the same window as last time.
704          */
705         if (id == GR_ROOT_WINDOW_ID)
706                 return rootwp;
707
708         if ((id == cachewindowid) && id)
709                 return cachewp;
710
711         /*
712          * No, search for it and cache it for future calls.
713          */
714         for (wp = listwp; wp; wp = wp->next) {
715                 if (wp->id == id) {
716                         cachewindowid = id;
717                         cachewp = wp;
718                         return wp;
719                 }
720         }
721
722         return NULL;
723 }
724
725
726
727 /*
728  * Return a pointer to the pixmap structure with the specified window id.
729  * Returns NULL if the pixmap does not exist.
730  */
731 GR_PIXMAP *
732 GsFindPixmap(GR_WINDOW_ID id)
733 {
734         GR_PIXMAP       *pp;            /* current pixmap pointer */
735
736         if ((id == cachepixmapid) && id)
737                 return cachepp;
738
739         /*
740          * No, search for it and cache it for future calls.
741          */
742         for (pp = listpp; pp; pp = pp->next) {
743                 if (pp->id == id) {
744                         cachepixmapid = id;
745                         cachepp = pp;
746                         return pp;
747                 }
748         }
749
750         return NULL;
751 }
752
753
754 /*
755  * Return a pointer to the graphics context with the specified id.
756  * Returns NULL if the graphics context does not exist, with an
757  * error saved.
758  */
759 GR_GC *
760 GsFindGC(GR_GC_ID gcid)
761 {
762         GR_GC           *gcp;           /* current graphics context pointer */
763
764         /*
765          * See if this is the same graphics context as last time.
766          */
767         if ((gcid == cachegcid) && gcid)
768                 return cachegcp;
769
770         /*
771          * No, search for it and cache it for future calls.
772          */
773         for (gcp = listgcp; gcp; gcp = gcp->next) {
774                 if (gcp->id == gcid) {
775                         cachegcid = gcid;
776                         cachegcp = gcp;
777                         return gcp;
778                 }
779         }
780
781         GsError(GR_ERROR_BAD_GC_ID, gcid);
782
783         return NULL;
784 }
785
786 /* Return a pointer to the region with the specified id.*/
787 GR_REGION *
788 GsFindRegion(GR_REGION_ID regionid)
789 {
790         GR_REGION       *regionp;       /* current region pointer */
791
792         for (regionp = listregionp; regionp; regionp = regionp->next) {
793                 if (regionp->id == regionid) {
794                         return regionp;
795                 }
796         }
797
798         return NULL;
799 }
800
801 /* find a font with specified id*/
802 GR_FONT *
803 GsFindFont(GR_FONT_ID fontid)
804 {
805         GR_FONT         *fontp;
806
807         for (fontp = listfontp; fontp; fontp = fontp->next) {
808                 if (fontp->id == fontid)
809                         return fontp;
810         }
811         return NULL;
812 }
813
814 /* find a cursor with specified id*/
815 GR_CURSOR *
816 GsFindCursor(GR_CURSOR_ID cursorid)
817 {
818         GR_CURSOR       *cursorp;
819
820         for (cursorp = listcursorp; cursorp; cursorp = cursorp->next) {
821                 if (cursorp->id == cursorid)
822                         return cursorp;
823         }
824         return NULL;
825 }
826
827 GR_TIMER *
828 GsFindTimer (GR_TIMER_ID timer_id)
829 {
830     GR_TIMER   *timer;
831     
832     /*
833      * See if this is the same graphics context as last time.
834      */
835     if ((timer_id == cache_timer_id) && timer_id)
836         return cache_timer;
837     
838     /*
839      * No, search for it and cache it for future calls.
840      */
841     for (timer = list_timer; timer != NULL; timer = timer->next) 
842     {
843         if (timer->id == timer_id) 
844         {
845             cache_timer_id = timer_id;
846             cache_timer = timer;
847             return timer;
848         }
849     }
850     return NULL;
851 }
852
853
854 /*
855  * Prepare to do drawing in a window or pixmap using the specified
856  * graphics context.  Returns the drawable pointer if successful,
857  * and the type of drawing id that was supplied.  Returns the special value
858  * GR_DRAW_TYPE_NONE if an error is generated, or if drawing is useless.
859  */
860 GR_DRAW_TYPE GsPrepareDrawing(GR_DRAW_ID id, GR_GC_ID gcid, GR_DRAWABLE **retdp)
861 {
862         GR_WINDOW       *wp;            /* found window */
863         GR_PIXMAP       *pp;            /* found pixmap */
864         GR_GC           *gcp;           /* found graphics context */
865         GR_FONT         *fontp;
866         GR_REGION       *regionp;       /* user clipping region */
867         MWCLIPREGION    *reg;
868         PMWFONT         pf;
869
870         *retdp = NULL;
871
872         gcp = GsFindGC(gcid);
873         if (gcp == NULL)
874                 return GR_DRAW_TYPE_NONE;
875
876         /*
877          * If the graphics context is not the current one, then
878          * make it the current one and remember to update it.
879          */
880         if (gcp != curgcp) {
881                 curgcp = gcp;
882                 gcp->changed = GR_TRUE;
883         }
884
885         /*
886          * Look for window or pixmap id
887          */
888         pp = NULL;
889         wp = GsFindWindow(id);
890         if (wp == NULL) {
891                 pp = GsFindPixmap(id);
892                 if (pp == NULL)
893                           return GR_DRAW_TYPE_NONE;
894            
895 #if DYNAMICREGIONS
896                 reg = GdAllocRectRegion(0, 0, pp->psd->xvirtres,
897                         pp->psd->yvirtres);
898                 /* intersect with user region if any*/
899                 if (gcp->regionid) {
900                         regionp = GsFindRegion(gcp->regionid);
901                         if (regionp)
902                                 GdIntersectRegion(reg, reg, regionp->rgn);
903                 }
904                 GdSetClipRegion(pp->psd, reg);
905 #else
906                 {
907                 MWCLIPRECT      cliprect;
908                 /* FIXME: setup pixmap clipping, different from windows*/
909                 cliprect.x = 0;
910                 cliprect.y = 0;
911                 cliprect.width = pp->psd->xvirtres;
912                 cliprect.height = pp->psd->yvirtres;
913                 GdSetClipRects(pp->psd, 1, &cliprect);
914                 }
915 #endif
916                 /* reset clip cache for next window draw*/
917                 clipwp = NULL;
918         } else {
919    
920                 if (!wp->output) {
921                           GsError(GR_ERROR_INPUT_ONLY_WINDOW, id);
922                           return GR_DRAW_TYPE_NONE;
923                 }
924
925                 if (wp->unmapcount)
926                           return GR_DRAW_TYPE_NONE;
927            
928                 /*
929                  * If the window is not the currently clipped one,
930                  * then make it the current one and define its clip rectangles.
931                  */
932                 if (wp != clipwp || gcp->changed) {
933                         /* find user region for intersect*/
934                         if (gcp->regionid)
935                                 regionp = GsFindRegion(gcp->regionid);
936                         else regionp = NULL;
937
938                         /*
939                          * Special handling if user region is not at offset 0,0
940                          */
941                         if (regionp && (gcp->xoff || gcp->yoff)) {
942                                 MWCLIPREGION *local = GdAllocRegion();
943
944                                 GdCopyRegion(local, regionp->rgn);
945                                 GdOffsetRegion(local, gcp->xoff, gcp->yoff);
946
947                                 GsSetClipWindow(wp, local,  
948                                         gcp->mode & ~GR_MODE_DRAWMASK);
949
950                                 GdDestroyRegion(local);
951                           } else {
952                                 GsSetClipWindow(wp, regionp? regionp->rgn: NULL,
953                                         gcp->mode & ~GR_MODE_DRAWMASK);
954                           }
955                 }
956         }
957
958         /*
959          * If the graphics context has been changed, then tell the
960          * device driver about it.
961          */
962         if (gcp->changed) {
963                 GdSetForeground(GdFindColor(gcp->foreground));
964                 GdSetBackground(GdFindColor(gcp->background));
965                 GdSetMode(gcp->mode & GR_MODE_DRAWMASK);
966                 GdSetUseBackground(gcp->usebackground);
967                 fontp = GsFindFont(gcp->fontid);
968                 pf = fontp? fontp->pfont: stdfont;
969                 GdSetFont(pf);
970                 gcp->changed = GR_FALSE;
971         }
972
973         *retdp = wp? (GR_DRAWABLE *)wp: (GR_DRAWABLE *)pp;
974         return wp? GR_DRAW_TYPE_WINDOW: GR_DRAW_TYPE_PIXMAP;
975 }
976
977 /*
978  * Prepare the specified window for drawing into it.
979  * This sets up the clipping regions to just allow drawing into it.
980  * Returns NULL if the drawing is illegal (with an error generated),
981  * or if the window is not mapped.
982  */
983 GR_WINDOW *GsPrepareWindow(GR_WINDOW_ID wid)
984 {
985         GR_WINDOW       *wp;            /* found window */
986
987         wp = GsFindWindow(wid);
988         if (wp == NULL)
989                 return NULL;
990         
991         if (!wp->output) {
992                 GsError(GR_ERROR_INPUT_ONLY_WINDOW, wid);
993                 return NULL;
994         }
995
996         if (wp->unmapcount)
997                 return NULL;
998
999         if (wp != clipwp) {
1000                 /* FIXME: no user region clipping here*/
1001                 GsSetClipWindow(wp, NULL, 0);
1002         }
1003
1004         return wp;
1005 }
1006
1007 /*
1008  * Find the window which is currently visible for the specified coordinates.
1009  * This just walks down the window tree looking for the deepest mapped
1010  * window which contains the specified point.  If the coordinates are
1011  * off the screen, the root window is returned.
1012  */
1013 GR_WINDOW *GsFindVisibleWindow(GR_COORD x, GR_COORD y)
1014 {
1015         GR_WINDOW       *wp;            /* current window */
1016         GR_WINDOW       *retwp;         /* returned window */
1017
1018         wp = rootwp;
1019         retwp = wp;
1020         while (wp) {
1021                 if ((wp->unmapcount == 0) && (wp->x <= x) && (wp->y <= y) &&
1022                         (wp->x + wp->width > x) && (wp->y + wp->height > y))
1023                 {
1024                         retwp = wp;
1025                         wp = wp->children;
1026                         continue;
1027                 }
1028                 wp = wp->siblings;
1029         }
1030         return retwp;
1031 }
1032
1033 /*
1034  * Check to see if the cursor shape is the correct shape for its current
1035  * location.  If not, its shape is changed.
1036  */
1037 void GsCheckCursor(void)
1038 {
1039         GR_WINDOW       *wp;            /* window cursor is in */
1040         GR_CURSOR       *cp;            /* cursor definition */
1041
1042         /*
1043          * Get the cursor at its current position, and if it is not the
1044          * currently defined one, then set the new cursor.  However,
1045          * if the pointer is currently grabbed, then leave it alone.
1046          */
1047         wp = grabbuttonwp;
1048         if (wp == NULL)
1049                 wp = mousewp;
1050
1051         cp = GsFindCursor(wp->cursorid);
1052         if (!cp)
1053                 cp = stdcursor;
1054         if (cp == curcursor)
1055                 return;
1056
1057         /*
1058          * It needs redefining, so do it.
1059          */
1060         curcursor = cp;
1061         GdMoveCursor(cursorx - cp->cursor.hotx, cursory - cp->cursor.hoty);
1062         GdSetCursor(&cp->cursor);
1063 }
1064
1065 /*
1066  * Check to see if the window the mouse is currently in has changed.
1067  * If so, generate enter and leave events as required.  The newest
1068  * mouse window is remembered in mousewp.  However, do not change the
1069  * window while it is grabbed.
1070  */
1071 void GsCheckMouseWindow(void)
1072 {
1073         GR_WINDOW       *wp;            /* newest window for mouse */
1074
1075         wp = grabbuttonwp;
1076         if (wp == NULL)
1077                 wp = GsFindVisibleWindow(cursorx, cursory);
1078         if (wp == mousewp)
1079                 return;
1080
1081         GsDeliverGeneralEvent(mousewp, GR_EVENT_TYPE_MOUSE_EXIT, NULL);
1082
1083         mousewp = wp;
1084
1085         GsDeliverGeneralEvent(wp, GR_EVENT_TYPE_MOUSE_ENTER, NULL);
1086 }
1087
1088 /*
1089  * Determine the current focus window for the current mouse coordinates.
1090  * The mouse coordinates only matter if the focus is not fixed.  Otherwise,
1091  * the selected window is dependant on the window which wants keyboard
1092  * events.  This also sets the current focus for that found window.
1093  * The window with focus is remembered in focuswp.
1094  */
1095 void GsCheckFocusWindow(void)
1096 {
1097         GR_WINDOW               *wp;            /* current window */
1098         GR_EVENT_CLIENT         *ecp;           /* current event client */
1099         GR_EVENT_MASK           eventmask;      /* event mask */
1100
1101         if (focusfixed)
1102                 return;
1103
1104         eventmask = GR_EVENT_MASK_KEY_DOWN;
1105
1106         /*
1107          * Walk upwards from the current window containing the mouse
1108          * looking for the first window which would accept a keyboard event.
1109          */
1110         for (wp = mousewp; ;wp = wp->parent) {
1111                 if (wp->props & GR_WM_PROPS_NOFOCUS)
1112                         continue;
1113                 for (ecp = wp->eventclients; ecp; ecp = ecp->next) {
1114                         if (ecp->eventmask & eventmask) {
1115                                 GsWpSetFocus(wp);
1116                                 return;
1117                         }
1118                 }
1119                 if ((wp == rootwp) || (wp->nopropmask & eventmask)) {
1120                         GsWpSetFocus(rootwp);
1121                         return;
1122                 }
1123         }
1124 }
1125
1126 /* Send an update activate event to top level window of passed window*/
1127 static void
1128 GsWpNotifyActivate(GR_WINDOW *wp)
1129 {
1130         GR_WINDOW       *pwp;
1131
1132         for (pwp=wp; pwp->parent; pwp=pwp->parent)
1133                 if (pwp->parent->id == GR_ROOT_WINDOW_ID)
1134                         break;
1135         if (pwp->id != GR_ROOT_WINDOW_ID)
1136                 GsDeliverUpdateEvent(pwp, GR_UPDATE_ACTIVATE, 0, 0, 0, 0);
1137 }
1138
1139 /*
1140  * Set the input focus to the specified window.
1141  * This generates focus out and focus in events as necessary.
1142  */
1143 void GsWpSetFocus(GR_WINDOW *wp)
1144 {
1145         GR_WINDOW       *oldfocus;
1146
1147         if (wp == focuswp)
1148                 return;
1149
1150         GsDeliverGeneralEvent(focuswp, GR_EVENT_TYPE_FOCUS_OUT, wp);
1151         GsWpNotifyActivate(focuswp);
1152
1153         oldfocus = focuswp;
1154         focuswp = wp;
1155
1156         GsDeliverGeneralEvent(wp, GR_EVENT_TYPE_FOCUS_IN, oldfocus);
1157         GsWpNotifyActivate(focuswp);
1158 }
1159
1160 /*
1161  * Set dynamic portrait mode and redraw screen.
1162  */
1163 void
1164 GsSetPortraitMode(int mode)
1165 {
1166         GdSetPortraitMode(&scrdev, mode);
1167         GdRestrictMouse(0, 0, scrdev.xvirtres - 1, scrdev.yvirtres - 1);
1168
1169         /* reset clip and root window size*/
1170         clipwp = NULL;
1171         rootwp->width = scrdev.xvirtres;
1172         rootwp->height = scrdev.yvirtres;
1173
1174         /* deliver portrait changed event to all windows selecting it*/
1175         GsDeliverPortraitChangedEvent();
1176         
1177         /* redraw screen - apps may redraw/resize again causing flicker*/
1178         GsRedrawScreen();
1179 }
1180
1181 /*
1182  * Check mouse coordinates and possibly set indicated portrait
1183  * mode from mouse position.
1184  */
1185 void
1186 GsSetPortraitModeFromXY(GR_COORD rootx, GR_COORD rooty)
1187 {
1188         int newmode;
1189
1190         if (rootx == 0) {
1191                 /* rotate left*/
1192                 switch (scrdev.portrait) {
1193                 case MWPORTRAIT_NONE:
1194                 default:
1195                         newmode = MWPORTRAIT_LEFT;
1196                         break;
1197                 case MWPORTRAIT_LEFT:
1198                         newmode = MWPORTRAIT_DOWN;
1199                         break;
1200                 case MWPORTRAIT_DOWN:
1201                         newmode = MWPORTRAIT_RIGHT;
1202                         break;
1203                 case MWPORTRAIT_RIGHT:
1204                         newmode = MWPORTRAIT_NONE;
1205                         break;
1206                 }
1207                 GsSetPortraitMode(newmode);
1208                 GrMoveCursor(5, rooty);
1209                 GdMoveMouse(5, rooty);
1210         } else if (rootx == scrdev.xvirtres-1) {
1211                 /* rotate right*/
1212                 switch (scrdev.portrait) {
1213                 case MWPORTRAIT_NONE:
1214                 default:
1215                         newmode = MWPORTRAIT_RIGHT;
1216                         break;
1217                 case MWPORTRAIT_LEFT:
1218                         newmode = MWPORTRAIT_NONE;
1219                         break;
1220                 case MWPORTRAIT_DOWN:
1221                         newmode = MWPORTRAIT_LEFT;
1222                         break;
1223                 case MWPORTRAIT_RIGHT:
1224                         newmode = MWPORTRAIT_DOWN;
1225                         break;
1226                 }
1227                 GsSetPortraitMode(newmode);
1228                 GrMoveCursor(scrdev.xvirtres-5, rooty);
1229                 GdMoveMouse(scrdev.xvirtres-5, rooty);
1230         }
1231 }