1 /**
2  * @file border.c
3  * @author Joe Wingbermuehle
4  * @date 2004-2015
5  *
6  * @brief Functions for handling window borders.
7  *
8  */
9 
10 #include "jwm.h"
11 #include "border.h"
12 #include "client.h"
13 #include "clientlist.h"
14 #include "color.h"
15 #include "icon.h"
16 #include "font.h"
17 #include "misc.h"
18 #include "settings.h"
19 #include "grab.h"
20 
21 static char *buttonNames[BI_COUNT];
22 static IconNode *buttonIcons[BI_COUNT];
23 
24 static void DrawBorderHelper(const ClientNode *np);
25 static void DrawBorderHandles(const ClientNode *np,
26                               Pixmap canvas, GC gc);
27 static void DrawBorderButtons(const ClientNode *np,
28                               Pixmap canvas, GC gc);
29 static char DrawBorderIcon(BorderIconType t,
30                            unsigned xoffset, unsigned yoffset,
31                            Pixmap canvas, long fg);
32 static void DrawCloseButton(unsigned xoffset, unsigned yoffset,
33                             Pixmap canvas, GC gc, long fg);
34 static void DrawMaxIButton(unsigned xoffset, unsigned yoffset,
35                            Pixmap canvas, GC gc, long fg);
36 static void DrawMaxAButton(unsigned xoffset, unsigned yoffset,
37                            Pixmap canvas, GC gc, long fg);
38 static void DrawMinButton(unsigned xoffset, unsigned yoffset,
39                           Pixmap canvas, GC gc, long fg);
40 static unsigned GetButtonCount(const ClientNode *np);
41 
42 #ifdef USE_SHAPE
43 static void FillRoundedRectangle(Drawable d, GC gc, int x, int y,
44                                  int width, int height, int radius);
45 #endif
46 
47 /** Initialize structures. */
InitializeBorders(void)48 void InitializeBorders(void)
49 {
50    memset(buttonNames, 0, sizeof(buttonNames));
51 }
52 
53 /** Initialize server resources. */
StartupBorders(void)54 void StartupBorders(void)
55 {
56    unsigned int i;
57 
58    for(i = 0; i < BI_COUNT; i++) {
59       if(buttonNames[i]) {
60          buttonIcons[i] = LoadNamedIcon(buttonNames[i], 1, 1);
61          Release(buttonNames[i]);
62          buttonNames[i] = NULL;
63       } else {
64          buttonIcons[i] = NULL;
65       }
66    }
67 
68    /* Always load a menu icon for windows without one. */
69    if(buttonIcons[BI_MENU] == NULL) {
70       buttonIcons[BI_MENU] = GetDefaultIcon();
71    }
72 }
73 
74 /** Destroy structures. */
DestroyBorders(void)75 void DestroyBorders(void)
76 {
77    unsigned i;
78    for(i = 0; i < BI_COUNT; i++)
79    {
80       if(buttonNames[i]) {
81          Release(buttonNames[i]);
82          buttonNames[i] = NULL;
83       }
84    }
85 }
86 
87 /** Get the size of the icon to display on a window. */
GetBorderIconSize(void)88 int GetBorderIconSize(void)
89 {
90    const unsigned height = GetTitleHeight();
91    if(settings.windowDecorations == DECO_MOTIF) {
92       return Max((int)height - 4, 0);
93    } else {
94       return Max((int)height - 6, 0);
95    }
96 }
97 
98 /** Determine the border action to take given coordinates. */
GetBorderActionType(const ClientNode * np,int x,int y)99 BorderActionType GetBorderActionType(const ClientNode *np, int x, int y)
100 {
101 
102    int north, south, east, west;
103    unsigned int resizeMask;
104    const unsigned int titleHeight = GetTitleHeight();
105 
106    GetBorderSize(&np->state, &north, &south, &east, &west);
107 
108    /* Check title bar actions. */
109    if((np->state.border & BORDER_TITLE) &&
110       titleHeight > settings.borderWidth) {
111 
112       /* Check buttons on the title bar. */
113       int offset = np->width + west;
114       if(y >= south && y <= titleHeight + south) {
115 
116          /* Menu button. */
117          if(np->width >= titleHeight) {
118             if(x > west && x <= titleHeight + west) {
119                return BA_MENU;
120             }
121          }
122 
123          /* Close button. */
124          if((np->state.border & BORDER_CLOSE) && offset > 2 * titleHeight) {
125             if(x > offset - titleHeight && x < offset) {
126                return BA_CLOSE;
127             }
128             offset -= titleHeight + 1;
129          }
130 
131          /* Maximize button. */
132          if((np->state.border & BORDER_MAX) && offset > 2 * titleHeight) {
133             if(x > offset - titleHeight && x < offset) {
134                return BA_MAXIMIZE;
135             }
136             offset -= titleHeight + 1;
137          }
138 
139          /* Minimize button. */
140          if((np->state.border & BORDER_MIN) && offset > 2 * titleHeight) {
141             if(x > offset - titleHeight && x < offset) {
142                return BA_MINIMIZE;
143             }
144          }
145 
146       }
147 
148       /* Check for move. */
149       if(y >= south && y <= titleHeight + south) {
150          if(x > west && x < offset) {
151             if(np->state.border & BORDER_MOVE) {
152                return BA_MOVE;
153             } else {
154                return BA_NONE;
155             }
156          }
157       }
158 
159    }
160 
161    /* Now we check resize actions.
162     * There is no need to go further if resizing isn't allowed. */
163    if(!(np->state.border & BORDER_RESIZE)) {
164       return BA_NONE;
165    }
166 
167    /* We don't allow resizing maximized windows. */
168    resizeMask = BA_RESIZE_S | BA_RESIZE_N
169               | BA_RESIZE_E | BA_RESIZE_W
170               | BA_RESIZE;
171    if(np->state.maxFlags & MAX_HORIZ) {
172       resizeMask &= ~(BA_RESIZE_E | BA_RESIZE_W);
173    }
174    if(np->state.maxFlags & MAX_VERT) {
175       resizeMask &= ~(BA_RESIZE_N | BA_RESIZE_S);
176    }
177    if(np->state.status & STAT_SHADED) {
178       resizeMask &= ~(BA_RESIZE_N | BA_RESIZE_S);
179    }
180 
181    /* Check south east/west and north east/west resizing. */
182    if(y > np->height + north - titleHeight) {
183       if(x < titleHeight) {
184          return (BA_RESIZE_S | BA_RESIZE_W | BA_RESIZE) & resizeMask;
185       } else if(x > np->width + west - titleHeight) {
186          return (BA_RESIZE_S | BA_RESIZE_E | BA_RESIZE) & resizeMask;
187       }
188    } else if(y < titleHeight) {
189       if(x < titleHeight) {
190          return (BA_RESIZE_N | BA_RESIZE_W | BA_RESIZE) & resizeMask;
191       } else if(x > np->width + west - titleHeight) {
192          return (BA_RESIZE_N | BA_RESIZE_E | BA_RESIZE) & resizeMask;
193       }
194    }
195 
196    /* Check east, west, north, and south resizing. */
197    if(x <= west) {
198       return (BA_RESIZE_W | BA_RESIZE) & resizeMask;
199    } else if(x >= np->width + west) {
200       return (BA_RESIZE_E | BA_RESIZE) & resizeMask;
201    } else if(y >= np->height + north) {
202       return (BA_RESIZE_S | BA_RESIZE) & resizeMask;
203    } else if(y <= south) {
204       return (BA_RESIZE_N | BA_RESIZE) & resizeMask;
205    } else {
206       return BA_NONE;
207    }
208 
209 }
210 
211 /** Reset the shape of a window border. */
ResetBorder(const ClientNode * np)212 void ResetBorder(const ClientNode *np)
213 {
214 #ifdef USE_SHAPE
215    Pixmap shapePixmap;
216    GC shapeGC;
217 #endif
218 
219    int north, south, east, west;
220    int width, height;
221 
222    if(np->parent == None) {
223       JXMoveResizeWindow(display, np->window, np->x, np->y,
224                          np->width, np->height);
225       return;
226    }
227 
228    GrabServer();
229 
230    /* Determine the size of the window. */
231    GetBorderSize(&np->state, &north, &south, &east, &west);
232    width = np->width + east + west;
233    if(np->state.status & STAT_SHADED) {
234       height = north + south;
235    } else {
236       height = np->height + north + south;
237    }
238 
239    /** Set the window size. */
240    if(!(np->state.status & STAT_SHADED)) {
241       JXMoveResizeWindow(display, np->window, west, north,
242                          np->width, np->height);
243    }
244    JXMoveResizeWindow(display, np->parent, np->x - west, np->y - north,
245                       width, height);
246 
247 #ifdef USE_SHAPE
248    if(settings.cornerRadius > 0 || (np->state.status & STAT_SHAPED)) {
249 
250       /* First set the shape to the window border. */
251       shapePixmap = JXCreatePixmap(display, np->parent, width, height, 1);
252       shapeGC = JXCreateGC(display, shapePixmap, 0, NULL);
253 
254       /* Make the whole area transparent. */
255       JXSetForeground(display, shapeGC, 0);
256       JXFillRectangle(display, shapePixmap, shapeGC, 0, 0, width, height);
257 
258       /* Draw the window area without the corners. */
259       /* Corner bound radius -1 to allow slightly better outline drawing */
260       JXSetForeground(display, shapeGC, 1);
261       if(((np->state.status & STAT_FULLSCREEN) || np->state.maxFlags) &&
262          !(np->state.status & (STAT_SHADED))) {
263          JXFillRectangle(display, shapePixmap, shapeGC, 0, 0, width, height);
264       } else {
265          FillRoundedRectangle(shapePixmap, shapeGC, 0, 0, width, height,
266                               settings.cornerRadius - 1);
267       }
268 
269       /* Apply the client window. */
270       if(!(np->state.status & STAT_SHADED) &&
271           (np->state.status & STAT_SHAPED)) {
272 
273          XRectangle *rects;
274          int count;
275          int ordering;
276 
277          /* Cut out an area for the client window. */
278          JXSetForeground(display, shapeGC, 0);
279          JXFillRectangle(display, shapePixmap, shapeGC, west, north,
280                          np->width, np->height);
281 
282          /* Fill in the visible area. */
283          rects = JXShapeGetRectangles(display, np->window, ShapeBounding,
284                                       &count, &ordering);
285          if(JLIKELY(rects)) {
286             int i;
287             for(i = 0; i < count; i++) {
288                rects[i].x += east;
289                rects[i].y += north;
290             }
291             JXSetForeground(display, shapeGC, 1);
292             JXFillRectangles(display, shapePixmap, shapeGC, rects, count);
293             JXFree(rects);
294          }
295 
296       }
297 
298       /* Set the shape. */
299       JXShapeCombineMask(display, np->parent, ShapeBounding, 0, 0,
300                          shapePixmap, ShapeSet);
301 
302       JXFreeGC(display, shapeGC);
303       JXFreePixmap(display, shapePixmap);
304    }
305 #endif
306 
307    UngrabServer();
308 
309 }
310 
311 /** Draw a client border. */
DrawBorder(ClientNode * np)312 void DrawBorder(ClientNode *np)
313 {
314 
315    Assert(np);
316 
317    /* Don't draw any more if we are shutting down. */
318    if(JUNLIKELY(shouldExit)) {
319       return;
320    }
321 
322    /* Must be either mapped or shaded to have a border. */
323    if(!(np->state.status & (STAT_MAPPED | STAT_SHADED))) {
324       return;
325    }
326 
327    /* Hidden and fullscreen windows don't get borders. */
328    if(np->state.status & (STAT_HIDDEN | STAT_FULLSCREEN)) {
329       return;
330    }
331 
332    /* Create the frame if needed. */
333    ReparentClient(np);
334 
335    /* Return if there is no border. */
336    if(np->parent == None) {
337       return;
338    }
339 
340    /* Do the actual drawing. */
341    DrawBorderHelper(np);
342 
343 }
344 
345 /** Helper method for drawing borders. */
DrawBorderHelper(const ClientNode * np)346 void DrawBorderHelper(const ClientNode *np)
347 {
348 
349    ColorType borderTextColor;
350 
351    long titleColor1, titleColor2;
352    long outlineColor;
353 
354    int north, south, east, west;
355    unsigned int width, height;
356 
357    unsigned int buttonCount;
358    int titleWidth, titleHeight;
359    Pixmap canvas;
360    GC gc;
361 
362    Assert(np);
363 
364    GetBorderSize(&np->state, &north, &south, &east, &west);
365    width = np->width + east + west;
366    height = np->height + north + south;
367 
368    /* Determine the colors and gradients to use. */
369    if(np->state.status & (STAT_ACTIVE | STAT_FLASH)) {
370 
371       borderTextColor = COLOR_TITLE_ACTIVE_FG;
372       titleColor1 = colors[COLOR_TITLE_ACTIVE_BG1];
373       titleColor2 = colors[COLOR_TITLE_ACTIVE_BG2];
374       outlineColor = colors[COLOR_TITLE_ACTIVE_DOWN];
375 
376    } else {
377 
378       borderTextColor = COLOR_TITLE_FG;
379       titleColor1 = colors[COLOR_TITLE_BG1];
380       titleColor2 = colors[COLOR_TITLE_BG2];
381       outlineColor = colors[COLOR_TITLE_DOWN];
382 
383    }
384 
385    /* Set parent background to reduce flicker. */
386    JXSetWindowBackground(display, np->parent, titleColor2);
387 
388    canvas = JXCreatePixmap(display, np->parent, width, north, rootDepth);
389    gc = JXCreateGC(display, canvas, 0, NULL);
390 
391    /* Clear the window with the right color. */
392    JXSetForeground(display, gc, titleColor2);
393    JXFillRectangle(display, canvas, gc, 0, 0, width, north);
394 
395    /* Determine how many pixels may be used for the title. */
396    buttonCount = GetButtonCount(np);
397    titleHeight = GetTitleHeight();
398    titleWidth = width - east - west - 5;
399    titleWidth -= titleHeight * (buttonCount + 1);
400    titleWidth -= settings.windowDecorations == DECO_MOTIF
401                ? (buttonCount + 1) : 0;
402 
403    /* Draw the top part (either a title or north border). */
404    if((np->state.border & BORDER_TITLE) &&
405       titleHeight > settings.borderWidth) {
406 
407       const unsigned startx = west + 1;
408       unsigned starty = 0;
409       if(settings.windowDecorations == DECO_MOTIF) {
410          if(!(np->state.maxFlags & (MAX_VERT | MAX_TOP))) {
411             starty += settings.borderWidth - 1;
412          }
413       }
414 
415       /* Draw a title bar. */
416       DrawHorizontalGradient(canvas, gc, titleColor1, titleColor2,
417                              0, 1, width, titleHeight - 2);
418 
419       /* Draw the icon. */
420 #ifdef USE_ICONS
421       if(np->width >= titleHeight) {
422          const int iconSize = GetBorderIconSize();
423          IconNode *icon = np->icon ? np->icon : buttonIcons[BI_MENU];
424          PutIcon(icon, canvas, colors[borderTextColor],
425                  startx, starty + (titleHeight - iconSize) / 2,
426                  iconSize, iconSize);
427       }
428 #endif
429 
430       if(np->name && np->name[0] && titleWidth > 0) {
431          const int sheight = GetStringHeight(FONT_BORDER);
432          const int textWidth = GetStringWidth(FONT_BORDER, np->name);
433          unsigned titlex, titley;
434          int xoffset = 0;
435          switch (settings.titleTextAlignment) {
436          case ALIGN_CENTER:
437             xoffset = (titleWidth - textWidth) / 2;
438             break;
439          case ALIGN_RIGHT:
440             xoffset = (titleWidth - textWidth);
441             break;
442          }
443          xoffset = Max(xoffset, 0);
444          titlex = startx + titleHeight + xoffset
445                 + (settings.windowDecorations == DECO_MOTIF ? 4 : 0);
446          titley = starty + (titleHeight - sheight) / 2;
447          RenderString(canvas, FONT_BORDER, borderTextColor,
448                       titlex, titley, titleWidth, np->name);
449       }
450 
451       DrawBorderButtons(np, canvas, gc);
452    }
453 
454    /* Copy the pixmap (for the title bar) to the window. */
455 
456    /* Copy the pixmap for the title bar and clear the part of
457     * the window to be drawn directly. */
458    if(settings.windowDecorations == DECO_MOTIF) {
459       const int off = np->state.maxFlags ? 0 : 2;
460       JXCopyArea(display, canvas, np->parent, gc, off, off,
461          width - 2 * off, north - off, off, off);
462       JXClearArea(display, np->parent,
463          off, north, width - 2 * off, height - north - off, False);
464    } else {
465       JXCopyArea(display, canvas, np->parent, gc, 1, 1,
466          width - 2, north - 1, 1, 1);
467       JXClearArea(display, np->parent,
468          1, north, width - 2, height - north - 1, False);
469    }
470 
471    /* Window outline. */
472    if(settings.windowDecorations == DECO_MOTIF) {
473       DrawBorderHandles(np, np->parent, gc);
474    } else {
475       JXSetForeground(display, gc, outlineColor);
476       if(np->state.status & STAT_SHADED) {
477          DrawRoundedRectangle(np->parent, gc, 0, 0, width - 1, north - 1,
478                               settings.cornerRadius);
479       } else if(np->state.maxFlags & MAX_HORIZ) {
480          if(!(np->state.maxFlags & (MAX_TOP | MAX_VERT))) {
481             /* Top */
482             JXDrawLine(display, np->parent, gc, 0, 0, width, 0);
483          }
484          if(!(np->state.maxFlags & (MAX_BOTTOM | MAX_VERT))) {
485             /* Bottom */
486             JXDrawLine(display, np->parent, gc,
487                        0, height - 1, width, height - 1);
488          }
489       } else if(np->state.maxFlags & MAX_VERT) {
490          if(!(np->state.maxFlags & (MAX_LEFT | MAX_HORIZ))) {
491             /* Left */
492             JXDrawLine(display, np->parent, gc, 0, 0, 0, height);
493          }
494          if(!(np->state.maxFlags & (MAX_RIGHT | MAX_HORIZ))) {
495             /* Right */
496             JXDrawLine(display, np->parent, gc, width - 1, 0,
497                width - 1, height);
498          }
499       } else {
500          DrawRoundedRectangle(np->parent, gc, 0, 0, width - 1, height - 1,
501                               settings.cornerRadius);
502       }
503    }
504 
505    JXFreePixmap(display, canvas);
506    JXFreeGC(display, gc);
507 
508 }
509 
510 /** Draw window handles. */
DrawBorderHandles(const ClientNode * np,Pixmap canvas,GC gc)511 void DrawBorderHandles(const ClientNode *np, Pixmap canvas, GC gc)
512 {
513    XSegment segments[9];
514    long pixelUp, pixelDown;
515    int width, height;
516    int north, south, east, west;
517    unsigned offset = 0;
518    unsigned starty = 0;
519    unsigned titleHeight;
520 
521    /* Determine the window size. */
522    GetBorderSize(&np->state, &north, &south, &east, &west);
523    titleHeight = GetTitleHeight();
524    width = np->width + east + west;
525    if(np->state.status & STAT_SHADED) {
526       height = north + south;
527    } else {
528       height = np->height + north + south;
529    }
530 
531    /* Determine the y-offset to start drawing. */
532    if(!(np->state.maxFlags & (MAX_VERT | MAX_TOP))) {
533       starty = settings.borderWidth;
534    }
535 
536    /* Determine the colors to use. */
537    if(np->state.status & (STAT_ACTIVE | STAT_FLASH)) {
538       pixelUp = colors[COLOR_TITLE_ACTIVE_UP];
539       pixelDown = colors[COLOR_TITLE_ACTIVE_DOWN];
540    } else {
541       pixelUp = colors[COLOR_TITLE_UP];
542       pixelDown = colors[COLOR_TITLE_DOWN];
543    }
544 
545    if(!(np->state.maxFlags & (MAX_VERT | MAX_TOP))) {
546       /* Top title border. */
547       segments[offset].x1 = west;
548       segments[offset].y1 = settings.borderWidth;
549       segments[offset].x2 = width - east - 1;
550       segments[offset].y2 = settings.borderWidth;
551       offset += 1;
552    }
553 
554    if(!(np->state.maxFlags & (MAX_HORIZ | MAX_RIGHT))) {
555       /* Right title border. */
556       segments[offset].x1 = west;
557       segments[offset].y1 = starty + 1;
558       segments[offset].x2 = east;
559       segments[offset].y2 = titleHeight + south - 1;
560       offset += 1;
561 
562       /* Inside right border. */
563       segments[offset].x1 = width - east;
564       segments[offset].y1 = starty;
565       segments[offset].x2 = width - east;
566       segments[offset].y2 = height - south;
567       offset += 1;
568    }
569 
570    if(!(np->state.maxFlags & (MAX_HORIZ | MAX_LEFT))) {
571       /* Inside left border. */
572       segments[offset].x1 = west;
573       segments[offset].y1 = starty;
574       segments[offset].x2 = west;
575       segments[offset].y2 = starty + titleHeight;
576       offset += 1;
577    }
578 
579    /* Inside bottom border. */
580    segments[offset].x1 = west;
581    segments[offset].y1 = height - south;
582    segments[offset].x2 = width - east;
583    segments[offset].y2 = height - south;
584    offset += 1;
585 
586    if(!(np->state.maxFlags & (MAX_HORIZ | MAX_LEFT))) {
587       /* Left border. */
588       segments[offset].x1 = 0;
589       segments[offset].y1 = 0;
590       segments[offset].x2 = 0;
591       segments[offset].y2 = height - 1;
592       offset += 1;
593       segments[offset].x1 = 1;
594       segments[offset].y1 = 1;
595       segments[offset].x2 = 1;
596       segments[offset].y2 = height - 2;
597       offset += 1;
598    }
599 
600    if(!(np->state.maxFlags & (MAX_VERT | MAX_TOP))) {
601       /* Top border. */
602       segments[offset].x1 = 1;
603       segments[offset].y1 = 0;
604       segments[offset].x2 = width - 1;
605       segments[offset].y2 = 0;
606       offset += 1;
607       segments[offset].x1 = 1;
608       segments[offset].y1 = 1;
609       segments[offset].x2 = width - 2;
610       segments[offset].y2 = 1;
611       offset += 1;
612    }
613 
614    /* Draw pixel-up segments. */
615    JXSetForeground(display, gc, pixelUp);
616    JXDrawSegments(display, canvas, gc, segments, offset);
617    offset = 0;
618 
619    /* Bottom title border. */
620    segments[offset].x1 = west + 1;
621    segments[offset].y1 = north - 1;
622    segments[offset].x2 = width - east - 1;
623    segments[offset].y2 = north - 1;
624    offset += 1;
625 
626    if(!(np->state.maxFlags & (MAX_HORIZ | MAX_RIGHT))) {
627       /* Right title border. */
628       segments[offset].x1 = width - east - 1;
629       segments[offset].y1 = starty + 1;
630       segments[offset].x2 = width - east - 1;
631       segments[offset].y2 = north - 1;
632       offset += 1;
633    }
634 
635    if(!(np->state.maxFlags & (MAX_VERT | MAX_TOP))) {
636       /* Inside top border. */
637       segments[offset].x1 = west - 1;
638       segments[offset].y1 = settings.borderWidth - 1;
639       segments[offset].x2 = width - east;
640       segments[offset].y2 = settings.borderWidth - 1;
641       offset += 1;
642    }
643 
644    if(!(np->state.maxFlags & (MAX_HORIZ | MAX_LEFT))) {
645       /* Inside left border. */
646       segments[offset].x1 = west - 1;
647       segments[offset].y1 = starty;
648       segments[offset].x2 = west - 1;
649       segments[offset].y2 = height - starty;
650       offset += 1;
651    }
652 
653    if(!(np->state.maxFlags & (MAX_HORIZ | MAX_RIGHT))) {
654       /* Right border. */
655       segments[offset].x1 = width - 1;
656       segments[offset].y1 = 0;
657       segments[offset].x2 = width - 1;
658       segments[offset].y2 = height - 1;
659       offset += 1;
660       segments[offset].x1 = width - 2;
661       segments[offset].y1 = 1;
662       segments[offset].x2 = width - 2;
663       segments[offset].y2 = height - 2;
664       offset += 1;
665    }
666 
667    if(!(np->state.maxFlags & (MAX_VERT | MAX_BOTTOM))) {
668       /* Bottom border. */
669       segments[offset].x1 = 0;
670       segments[offset].y1 = height - 1;
671       segments[offset].x2 = width;
672       segments[offset].y2 = height - 1;
673       offset += 1;
674       segments[offset].x1 = 1;
675       segments[offset].y1 = height - 2;
676       segments[offset].x2 = width - 1;
677       segments[offset].y2 = height - 2;
678       offset += 1;
679    }
680 
681    /* Draw pixel-down segments. */
682    JXSetForeground(display, gc, pixelDown);
683    JXDrawSegments(display, canvas, gc, segments, offset);
684    offset = 0;
685 
686    /* Draw marks */
687    if((np->state.border & BORDER_RESIZE)
688       && !(np->state.status & STAT_SHADED)
689       && !(np->state.maxFlags)) {
690 
691       /* Upper left */
692       segments[0].x1 = titleHeight + settings.borderWidth - 1;
693       segments[0].y1 = 0;
694       segments[0].x2 = titleHeight + settings.borderWidth - 1;
695       segments[0].y2 = settings.borderWidth;
696       segments[1].x1 = 0;
697       segments[1].y1 = titleHeight + settings.borderWidth - 1;
698       segments[1].x2 = settings.borderWidth;
699       segments[1].y2 = titleHeight + settings.borderWidth - 1;
700 
701       /* Upper right. */
702       segments[2].x1 = width - settings.borderWidth;
703       segments[2].y1 = titleHeight + settings.borderWidth - 1;
704       segments[2].x2 = width;
705       segments[2].y2 = titleHeight + settings.borderWidth - 1;
706       segments[3].x1 = width - titleHeight - settings.borderWidth - 1;
707       segments[3].y1 = 0;
708       segments[3].x2 = width - titleHeight - settings.borderWidth - 1;
709       segments[3].y2 = settings.borderWidth;
710 
711       /* Lower left */
712       segments[4].x1 = 0;
713       segments[4].y1 = height - titleHeight - settings.borderWidth - 1;
714       segments[4].x2 = settings.borderWidth;
715       segments[4].y2 = height - titleHeight - settings.borderWidth - 1;
716       segments[5].x1 = titleHeight + settings.borderWidth - 1;
717       segments[5].y1 = height - settings.borderWidth;
718       segments[5].x2 = titleHeight + settings.borderWidth - 1;
719       segments[5].y2 = height;
720 
721       /* Lower right */
722       segments[6].x1 = width - settings.borderWidth;
723       segments[6].y1 = height - titleHeight - settings.borderWidth - 1;
724       segments[6].x2 = width;
725       segments[6].y2 = height - titleHeight - settings.borderWidth - 1;
726       segments[7].x1 = width - titleHeight - settings.borderWidth - 1;
727       segments[7].y1 = height - settings.borderWidth;
728       segments[7].x2 = width - titleHeight - settings.borderWidth - 1;
729       segments[7].y2 = height;
730 
731       /* Draw pixel-down segments. */
732       JXSetForeground(display, gc, pixelDown);
733       JXDrawSegments(display, canvas, gc, segments, 8);
734 
735       /* Upper left */
736       segments[0].x1 = titleHeight + settings.borderWidth;
737       segments[0].y1 = 0;
738       segments[0].x2 = titleHeight + settings.borderWidth;
739       segments[0].y2 = settings.borderWidth;
740       segments[1].x1 = 0;
741       segments[1].y1 = titleHeight + settings.borderWidth;
742       segments[1].x2 = settings.borderWidth;
743       segments[1].y2 = titleHeight + settings.borderWidth;
744 
745       /* Upper right */
746       segments[2].x1 = width - titleHeight - settings.borderWidth;
747       segments[2].y1 = 0;
748       segments[2].x2 = width - titleHeight - settings.borderWidth;
749       segments[2].y2 = settings.borderWidth;
750       segments[3].x1 = width - settings.borderWidth;
751       segments[3].y1 = titleHeight + settings.borderWidth;
752       segments[3].x2 = width;
753       segments[3].y2 = titleHeight + settings.borderWidth;
754 
755       /* Lower left */
756       segments[4].x1 = 0;
757       segments[4].y1 = height - titleHeight - settings.borderWidth;
758       segments[4].x2 = settings.borderWidth;
759       segments[4].y2 = height - titleHeight - settings.borderWidth;
760       segments[5].x1 = titleHeight + settings.borderWidth;
761       segments[5].y1 = height - settings.borderWidth;
762       segments[5].x2 = titleHeight + settings.borderWidth;
763       segments[5].y2 = height;
764 
765       /* Lower right */
766       segments[6].x1 = width - settings.borderWidth;
767       segments[6].y1 = height - titleHeight - settings.borderWidth;
768       segments[6].x2 = width;
769       segments[6].y2 = height - titleHeight - settings.borderWidth;
770       segments[7].x1 = width - titleHeight - settings.borderWidth;
771       segments[7].y1 = height - settings.borderWidth;
772       segments[7].x2 = width - titleHeight - settings.borderWidth;
773       segments[7].y2 = height;
774 
775       /* Draw pixel-up segments. */
776       JXSetForeground(display, gc, pixelUp);
777       JXDrawSegments(display, canvas, gc, segments, 8);
778    }
779 }
780 
781 /** Determine the number of buttons to be displayed for a client. */
GetButtonCount(const ClientNode * np)782 unsigned GetButtonCount(const ClientNode *np)
783 {
784 
785    int north, south, east, west;
786    unsigned count;
787    unsigned buttonWidth;
788    int available;
789    const unsigned titleHeight = GetTitleHeight();
790 
791    if(!(np->state.border & BORDER_TITLE)) {
792       return 0;
793    }
794    if(titleHeight <= settings.borderWidth) {
795       return 0;
796    }
797 
798    buttonWidth = titleHeight;
799    buttonWidth += settings.windowDecorations == DECO_MOTIF ? 1 : 0;
800 
801    GetBorderSize(&np->state, &north, &south, &east, &west);
802 
803    count = 0;
804    available = np->width - buttonWidth;
805    if(available < buttonWidth) {
806       return count;
807    }
808 
809    if(np->state.border & BORDER_CLOSE) {
810       count += 1;
811       available -= buttonWidth;
812       if(available < buttonWidth) {
813          return count;
814       }
815    }
816 
817    if(np->state.border & BORDER_MAX) {
818       count += 1;
819       available -= buttonWidth;
820       if(available < buttonWidth) {
821          return count;
822       }
823    }
824 
825    if(np->state.border & BORDER_MIN) {
826       count += 1;
827    }
828 
829    return count;
830 }
831 
832 /** Draw the buttons on a client frame. */
DrawBorderButtons(const ClientNode * np,Pixmap canvas,GC gc)833 void DrawBorderButtons(const ClientNode *np, Pixmap canvas, GC gc)
834 {
835    long color;
836    long pixelUp, pixelDown;
837    const unsigned titleHeight = GetTitleHeight();
838    int xoffset, yoffset;
839    int north, south, east, west;
840    int minx;
841 
842    GetBorderSize(&np->state, &north, &south, &east, &west);
843    xoffset = np->width + Min(east, west) - titleHeight;
844    minx = titleHeight + east;
845    if(xoffset <= minx) {
846       return;
847    }
848 
849    /* Determine the colors to use. */
850    if(np->state.status & (STAT_ACTIVE | STAT_FLASH)) {
851       color = colors[COLOR_TITLE_ACTIVE_FG];
852       pixelUp = colors[COLOR_TITLE_ACTIVE_UP];
853       pixelDown = colors[COLOR_TITLE_ACTIVE_DOWN];
854    } else {
855       color = colors[COLOR_TITLE_FG];
856       pixelUp = colors[COLOR_TITLE_UP];
857       pixelDown = colors[COLOR_TITLE_DOWN];
858    }
859 
860    yoffset = 0;
861    if(settings.windowDecorations == DECO_MOTIF) {
862       if(!(np->state.maxFlags & (MAX_TOP | MAX_VERT))) {
863          yoffset += settings.borderWidth - 1;
864       }
865    }
866 
867    if(settings.windowDecorations == DECO_MOTIF) {
868       JXSetForeground(display, gc, pixelDown);
869       JXDrawLine(display, canvas, gc,
870                       west + titleHeight - 1,
871                       yoffset,
872                       west + titleHeight - 1,
873                       yoffset + titleHeight);
874       JXSetForeground(display, gc, pixelUp);
875       JXDrawLine(display, canvas, gc,
876                  west + titleHeight,
877                  yoffset,
878                  west + titleHeight,
879                  yoffset + titleHeight);
880    }
881 
882    /* Close button. */
883    if(np->state.border & BORDER_CLOSE) {
884 
885       JXSetForeground(display, gc, color);
886       DrawCloseButton(xoffset, yoffset, canvas, gc, color);
887 
888       if(settings.windowDecorations == DECO_MOTIF) {
889          JXSetForeground(display, gc, pixelDown);
890          JXDrawLine(display, canvas, gc, xoffset - 1,
891                     yoffset, xoffset - 1,
892                     yoffset + titleHeight);
893          JXSetForeground(display, gc, pixelUp);
894          JXDrawLine(display, canvas, gc, xoffset,
895                     yoffset, xoffset, yoffset + titleHeight);
896          xoffset -= 1;
897       }
898 
899       xoffset -= titleHeight;
900       if(xoffset <= minx) {
901          return;
902       }
903    }
904 
905    /* Maximize button. */
906    if(np->state.border & BORDER_MAX) {
907 
908       JXSetForeground(display, gc, color);
909       if(np->state.maxFlags) {
910          DrawMaxAButton(xoffset, yoffset, canvas, gc, color);
911       } else {
912          DrawMaxIButton(xoffset, yoffset, canvas, gc, color);
913       }
914 
915       if(settings.windowDecorations == DECO_MOTIF) {
916          JXSetForeground(display, gc, pixelDown);
917          JXDrawLine(display, canvas, gc, xoffset - 1,
918                     yoffset, xoffset - 1,
919                     yoffset + titleHeight);
920          JXSetForeground(display, gc, pixelUp);
921          JXDrawLine(display, canvas, gc, xoffset,
922                     yoffset, xoffset, yoffset + titleHeight);
923          xoffset -= 1;
924       }
925 
926       xoffset -= titleHeight;
927       if(xoffset <= minx) {
928          return;
929       }
930    }
931 
932    /* Minimize button. */
933    if(np->state.border & BORDER_MIN) {
934 
935       JXSetForeground(display, gc, color);
936       DrawMinButton(xoffset, yoffset, canvas, gc, color);
937 
938       if(settings.windowDecorations == DECO_MOTIF) {
939          JXSetForeground(display, gc, pixelDown);
940          JXDrawLine(display, canvas, gc, xoffset - 1,
941                     yoffset, xoffset - 1,
942                     yoffset + titleHeight);
943          JXSetForeground(display, gc, pixelUp);
944          JXDrawLine(display, canvas, gc, xoffset,
945                     yoffset, xoffset, yoffset + titleHeight);
946          xoffset -= 1;
947       }
948    }
949 }
950 
951 /** Attempt to draw a border icon. */
DrawBorderIcon(BorderIconType t,unsigned xoffset,unsigned yoffset,Pixmap canvas,long fg)952 char DrawBorderIcon(BorderIconType t,
953                     unsigned xoffset, unsigned yoffset,
954                     Pixmap canvas, long fg)
955 {
956    if(buttonIcons[t]) {
957 #ifdef USE_ICONS
958       const unsigned titleHeight = GetTitleHeight();
959       PutIcon(buttonIcons[t], canvas, fg, xoffset + 2, yoffset + 2,
960               titleHeight - 4, titleHeight - 4);
961 #endif
962       return 1;
963    } else {
964       return 0;
965    }
966 }
967 
968 /** Draw a close button. */
DrawCloseButton(unsigned xoffset,unsigned yoffset,Pixmap canvas,GC gc,long fg)969 void DrawCloseButton(unsigned xoffset, unsigned yoffset,
970                      Pixmap canvas, GC gc, long fg)
971 {
972    XSegment segments[2];
973    const unsigned titleHeight = GetTitleHeight();
974    unsigned size;
975    unsigned x1, y1;
976    unsigned x2, y2;
977 
978    if(DrawBorderIcon(BI_CLOSE, xoffset, yoffset, canvas, fg)) {
979       return;
980    }
981 
982    size = (titleHeight + 2) / 3;
983    x1 = xoffset + titleHeight / 2 - size / 2;
984    y1 = yoffset + titleHeight / 2 - size / 2;
985    x2 = x1 + size;
986    y2 = y1 + size;
987 
988    segments[0].x1 = x1;
989    segments[0].y1 = y1;
990    segments[0].x2 = x2;
991    segments[0].y2 = y2;
992 
993    segments[1].x1 = x2;
994    segments[1].y1 = y1;
995    segments[1].x2 = x1;
996    segments[1].y2 = y2;
997 
998    JXSetLineAttributes(display, gc, 2, LineSolid,
999                        CapProjecting, JoinBevel);
1000    JXDrawSegments(display, canvas, gc, segments, 2);
1001    JXSetLineAttributes(display, gc, 1, LineSolid,
1002                        CapNotLast, JoinMiter);
1003 
1004 }
1005 
1006 /** Draw an inactive maximize button. */
DrawMaxIButton(unsigned xoffset,unsigned yoffset,Pixmap canvas,GC gc,long fg)1007 void DrawMaxIButton(unsigned xoffset, unsigned yoffset,
1008                     Pixmap canvas, GC gc, long fg)
1009 {
1010 
1011    XSegment segments[5];
1012    const unsigned titleHeight = GetTitleHeight();
1013    unsigned int size;
1014    unsigned int x1, y1;
1015    unsigned int x2, y2;
1016 
1017    if(DrawBorderIcon(BI_MAX, xoffset, yoffset, canvas, fg)) {
1018       return;
1019    }
1020 
1021    size = 2 + (titleHeight + 2) / 3;
1022    x1 = xoffset + titleHeight / 2 - size / 2;
1023    y1 = yoffset + titleHeight / 2 - size / 2;
1024    x2 = x1 + size;
1025    y2 = y1 + size;
1026 
1027    segments[0].x1 = x1;
1028    segments[0].y1 = y1;
1029    segments[0].x2 = x1 + size;
1030    segments[0].y2 = y1;
1031 
1032    segments[1].x1 = x1;
1033    segments[1].y1 = y1 + 1;
1034    segments[1].x2 = x1 + size;
1035    segments[1].y2 = y1 + 1;
1036 
1037    segments[2].x1 = x1;
1038    segments[2].y1 = y1;
1039    segments[2].x2 = x1;
1040    segments[2].y2 = y2;
1041 
1042    segments[3].x1 = x2;
1043    segments[3].y1 = y1;
1044    segments[3].x2 = x2;
1045    segments[3].y2 = y2;
1046 
1047    segments[4].x1 = x1;
1048    segments[4].y1 = y2;
1049    segments[4].x2 = x2;
1050    segments[4].y2 = y2;
1051 
1052    JXSetLineAttributes(display, gc, 1, LineSolid,
1053                        CapProjecting, JoinMiter);
1054    JXDrawSegments(display, canvas, gc, segments, 5);
1055    JXSetLineAttributes(display, gc, 1, LineSolid,
1056                        CapButt, JoinMiter);
1057 
1058 }
1059 
1060 /** Draw an active maximize button. */
DrawMaxAButton(unsigned xoffset,unsigned yoffset,Pixmap canvas,GC gc,long fg)1061 void DrawMaxAButton(unsigned xoffset, unsigned yoffset,
1062                     Pixmap canvas, GC gc, long fg)
1063 {
1064    XSegment segments[8];
1065    unsigned titleHeight;
1066    unsigned size;
1067    unsigned x1, y1;
1068    unsigned x2, y2;
1069    unsigned x3, y3;
1070 
1071    if(DrawBorderIcon(BI_MAX_ACTIVE, xoffset, yoffset, canvas, fg)) {
1072       return;
1073    }
1074 
1075    titleHeight = GetTitleHeight();
1076    size = 2 + (titleHeight + 2) / 3;
1077    x1 = xoffset + titleHeight / 2 - size / 2;
1078    y1 = yoffset + titleHeight / 2 - size / 2;
1079    x2 = x1 + size;
1080    y2 = y1 + size;
1081    x3 = x1 + size / 2;
1082    y3 = y1 + size / 2;
1083 
1084    segments[0].x1 = x1;
1085    segments[0].y1 = y1;
1086    segments[0].x2 = x2;
1087    segments[0].y2 = y1;
1088 
1089    segments[1].x1 = x1;
1090    segments[1].y1 = y1 + 1;
1091    segments[1].x2 = x2;
1092    segments[1].y2 = y1 + 1;
1093 
1094    segments[2].x1 = x1;
1095    segments[2].y1 = y1;
1096    segments[2].x2 = x1;
1097    segments[2].y2 = y2;
1098 
1099    segments[3].x1 = x2;
1100    segments[3].y1 = y1;
1101    segments[3].x2 = x2;
1102    segments[3].y2 = y2;
1103 
1104    segments[4].x1 = x1;
1105    segments[4].y1 = y2;
1106    segments[4].x2 = x2;
1107    segments[4].y2 = y2;
1108 
1109    segments[5].x1 = x1;
1110    segments[5].y1 = y3;
1111    segments[5].x2 = x3;
1112    segments[5].y2 = y3;
1113 
1114    segments[6].x1 = x1;
1115    segments[6].y1 = y3 + 1;
1116    segments[6].x2 = x3;
1117    segments[6].y2 = y3 + 1;
1118 
1119    segments[7].x1 = x3;
1120    segments[7].y1 = y3;
1121    segments[7].x2 = x3;
1122    segments[7].y2 = y2;
1123 
1124    JXSetLineAttributes(display, gc, 1, LineSolid,
1125                        CapProjecting, JoinMiter);
1126    JXDrawSegments(display, canvas, gc, segments, 8);
1127    JXSetLineAttributes(display, gc, 1, LineSolid,
1128                        CapButt, JoinMiter);
1129 }
1130 
1131 /** Draw a minimize button. */
DrawMinButton(unsigned xoffset,unsigned yoffset,Pixmap canvas,GC gc,long fg)1132 void DrawMinButton(unsigned xoffset, unsigned yoffset,
1133                    Pixmap canvas, GC gc, long fg)
1134 {
1135    unsigned titleHeight;
1136    unsigned size;
1137    unsigned x1, y1;
1138    unsigned x2, y2;
1139 
1140    if(DrawBorderIcon(BI_MIN, xoffset, yoffset, canvas, fg)) {
1141       return;
1142    }
1143 
1144    titleHeight = GetTitleHeight();
1145    size = (titleHeight + 2) / 3;
1146    x1 = xoffset + titleHeight / 2 - size / 2;
1147    y1 = yoffset + titleHeight / 2 - size / 2;
1148    x2 = x1 + size;
1149    y2 = y1 + size;
1150    JXSetLineAttributes(display, gc, 2, LineSolid,
1151                        CapProjecting, JoinMiter);
1152    JXDrawLine(display, canvas, gc, x1, y2, x2, y2);
1153    JXSetLineAttributes(display, gc, 1, LineSolid, CapButt, JoinMiter);
1154 
1155 }
1156 
1157 /** Redraw the borders on the current desktop.
1158  * This should be done after loading clients since the stacking order
1159  * may cause borders on the current desktop to become visible after moving
1160  * clients to their assigned desktops.
1161  */
ExposeCurrentDesktop(void)1162 void ExposeCurrentDesktop(void)
1163 {
1164    ClientNode *np;
1165    int layer;
1166 
1167    for(layer = 0; layer < LAYER_COUNT; layer++) {
1168       for(np = nodes[layer]; np; np = np->next) {
1169          if(!(np->state.status & (STAT_HIDDEN | STAT_MINIMIZED))) {
1170             DrawBorder(np);
1171          }
1172       }
1173    }
1174 }
1175 
1176 /** Get the height of a window title bar. */
GetTitleHeight(void)1177 unsigned GetTitleHeight(void)
1178 {
1179    if(JUNLIKELY(settings.titleHeight == 0)) {
1180       settings.titleHeight = GetStringHeight(FONT_BORDER) + 4;
1181    }
1182    return settings.titleHeight;
1183 }
1184 
1185 /** Get the size of the borders for a client. */
GetBorderSize(const ClientState * state,int * north,int * south,int * east,int * west)1186 void GetBorderSize(const ClientState *state,
1187                    int *north, int *south, int *east, int *west)
1188 {
1189    Assert(state);
1190    Assert(north);
1191    Assert(south);
1192    Assert(east);
1193    Assert(west);
1194 
1195    /* Full screen is a special case. */
1196    if(state->status & STAT_FULLSCREEN) {
1197       *north = 0;
1198       *south = 0;
1199       *east = 0;
1200       *west = 0;
1201       return;
1202    }
1203 
1204    if(state->border & BORDER_OUTLINE) {
1205 
1206       if(state->border & BORDER_TITLE) {
1207          *north = GetTitleHeight();
1208       } else if(settings.windowDecorations == DECO_MOTIF) {
1209          *north = 0;
1210       } else {
1211          *north = settings.borderWidth;
1212          if(state->maxFlags & (MAX_VERT | MAX_TOP)) {
1213             *north = Max(0, *north - 1);
1214          }
1215       }
1216       if(state->maxFlags & MAX_VERT) {
1217          *south = 0;
1218       } else {
1219          if(settings.windowDecorations == DECO_MOTIF) {
1220             if(!(state->maxFlags & MAX_TOP)) {
1221                *north += settings.borderWidth;
1222             }
1223             if(!(state->maxFlags & MAX_BOTTOM)) {
1224                *south = settings.borderWidth;
1225             } else {
1226                *south = 0;
1227             }
1228          } else {
1229             if(state->status & STAT_SHADED) {
1230                *south = 0;
1231             } else {
1232                *south = settings.borderWidth;
1233             }
1234          }
1235       }
1236 
1237       if(state->maxFlags & (MAX_HORIZ | MAX_LEFT)) {
1238          *west = 0;
1239       } else {
1240          *west = settings.borderWidth;
1241       }
1242       if(state->maxFlags & (MAX_HORIZ | MAX_RIGHT)) {
1243          *east = 0;
1244       } else {
1245          *east = settings.borderWidth;
1246       }
1247 
1248    } else {
1249 
1250       *north = 0;
1251       *south = 0;
1252       *east = 0;
1253       *west = 0;
1254 
1255    }
1256 }
1257 
1258 /** Draw a rounded rectangle. */
DrawRoundedRectangle(Drawable d,GC gc,int x,int y,int width,int height,int radius)1259 void DrawRoundedRectangle(Drawable d, GC gc, int x, int y,
1260                           int width, int height, int radius)
1261 {
1262 #ifdef USE_SHAPE
1263 #ifdef USE_XMU
1264 
1265    if(radius > 0) {
1266       XmuDrawRoundedRectangle(display, d, gc, x, y, width, height,
1267                               radius, radius);
1268    } else {
1269       JXDrawRectangle(display, d, gc, x, y, width, height);
1270    }
1271 
1272 #else
1273 
1274    if(radius > 0) {
1275       XSegment segments[4];
1276       XArc     arcs[4];
1277 
1278       segments[0].x1 = x + radius;         segments[0].y1 = y;
1279       segments[0].x2 = x + width - radius; segments[0].y2 = y;
1280       segments[1].x1 = x + radius;         segments[1].y1 = y + height;
1281       segments[1].x2 = x + width - radius; segments[1].y2 = y + height;
1282       segments[2].x1 = x;                  segments[2].y1 = y + radius;
1283       segments[2].x2 = x;                  segments[2].y2 = y + height - radius;
1284       segments[3].x1 = x + width;          segments[3].y1 = y + radius;
1285       segments[3].x2 = x + width;          segments[3].y2 = y + height - radius;
1286       JXDrawSegments(display, d, gc, segments, 4);
1287 
1288       arcs[0].x = x;
1289       arcs[0].y = y;
1290       arcs[0].width = radius * 2;
1291       arcs[0].height = radius * 2;
1292       arcs[0].angle1 = 90 * 64;
1293       arcs[0].angle2 = 90 * 64;
1294       arcs[1].x = x + width - radius * 2;
1295       arcs[1].y = y;
1296       arcs[1].width  = radius * 2;
1297       arcs[1].height = radius * 2;
1298       arcs[1].angle1 = 0 * 64;
1299       arcs[1].angle2 = 90 * 64;
1300       arcs[2].x = x;
1301       arcs[2].y = y + height - radius * 2;
1302       arcs[2].width  = radius * 2;
1303       arcs[2].height = radius * 2;
1304       arcs[2].angle1 = 180 * 64;
1305       arcs[2].angle2 = 90 * 64;
1306       arcs[3].x = x + width - radius * 2;
1307       arcs[3].y = y + height - radius * 2;
1308       arcs[3].width  = radius * 2;
1309       arcs[3].height = radius * 2;
1310       arcs[3].angle1 = 270 * 64;
1311       arcs[3].angle2 = 90 * 64;
1312       JXDrawArcs(display, d, gc, arcs, 4);
1313    } else {
1314       JXDrawRectangle(display, d, gc, x, y, width, height);
1315    }
1316 
1317 #endif
1318 #else
1319 
1320    JXDrawRectangle(display, d, gc, x, y, width, height);
1321 
1322 #endif
1323 }
1324 
1325 /** Fill a rounded rectangle. */
1326 #ifdef USE_SHAPE
FillRoundedRectangle(Drawable d,GC gc,int x,int y,int width,int height,int radius)1327 void FillRoundedRectangle(Drawable d, GC gc, int x, int y,
1328                           int width, int height, int radius)
1329 {
1330 
1331 #ifdef USE_XMU
1332 
1333    XmuFillRoundedRectangle(display, d, gc, x, y, width, height,
1334                            radius, radius);
1335 
1336 #else
1337 
1338    XRectangle  rects[3];
1339    XArc        arcs[4];
1340 
1341    rects[0].x = x + radius;
1342    rects[0].y = y;
1343    rects[0].width = width - radius * 2;
1344    rects[0].height = radius;
1345    rects[1].x = x;
1346    rects[1].y = radius;
1347    rects[1].width = width;
1348    rects[1].height = height - radius * 2;
1349    rects[2].x = x + radius;
1350    rects[2].y = y + height - radius;
1351    rects[2].width = width - radius * 2;
1352    rects[2].height = radius;
1353    JXFillRectangles(display, d, gc, rects, 3);
1354 
1355    arcs[0].x = x;
1356    arcs[0].y = y;
1357    arcs[0].width = radius * 2;
1358    arcs[0].height = radius * 2;
1359    arcs[0].angle1 = 90 * 64;
1360    arcs[0].angle2 = 90 * 64;
1361    arcs[1].x = x + width - radius * 2 - 1;
1362    arcs[1].y = y;
1363    arcs[1].width  = radius * 2;
1364    arcs[1].height = radius * 2;
1365    arcs[1].angle1 = 0 * 64;
1366    arcs[1].angle2 = 90 * 64;
1367    arcs[2].x = x;
1368    arcs[2].y = y + height - radius * 2 - 1;
1369    arcs[2].width  = radius * 2;
1370    arcs[2].height = radius * 2;
1371    arcs[2].angle1 = 180 * 64;
1372    arcs[2].angle2 = 90 * 64;
1373    arcs[3].x = x + width - radius * 2 - 1;
1374    arcs[3].y = y + height - radius * 2 -1;
1375    arcs[3].width  = radius * 2;
1376    arcs[3].height = radius * 2;
1377    arcs[3].angle1 = 270 * 64;
1378    arcs[3].angle2 = 90 * 64;
1379    JXFillArcs(display, d, gc, arcs, 4);
1380 
1381 #endif
1382 
1383 }
1384 #endif
1385 
1386 /** Set the icon to use for a button. */
SetBorderIcon(BorderIconType t,const char * name)1387 void SetBorderIcon(BorderIconType t, const char *name)
1388 {
1389    if(buttonNames[t]) {
1390       Release(buttonNames[t]);
1391    }
1392    buttonNames[t] = CopyString(name);
1393 }
1394 
1395