CTWM
Loading...
Searching...
No Matches
/usr/src/RPM/BUILD/ctwm-4.1.0/win_decorations.c
Go to the documentation of this file.
1/*
2 * Window decoration routines
3 */
4
5
6#include "ctwm.h"
7
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11
12#include <X11/extensions/shape.h>
13
14#include "gram.tab.h"
15#include "image.h"
16#include "iconmgr.h"
17#include "screen.h"
18#include "drawing.h"
19#include "occupation.h"
20#include "r_area.h"
21#include "r_layout.h"
22#include "win_utils.h"
23#include "workspace_manager.h"
24
25#include "win_decorations.h"
26
27
28/* Internal bits */
29static void ComputeWindowTitleOffsets(TwmWindow *tmp_win, unsigned int width,
30 bool squeeze);
33
35static void Draw3DCorner(Window w, int x, int y, int width, int height,
36 int thick, int bw, ColorPair cp, CornerType type);
37
38
39
40/*
41 * First, the bits for setting up the frame window
42 */
43
44/***********************************************************************
45 *
46 * Procedure:
47 * SetupWindow - set window sizes, this was called from either
48 * AddWindow, EndResize, or HandleConfigureNotify.
49 *
50 * Inputs:
51 * tmp_win - the TwmWindow pointer
52 * x - the x coordinate of the upper-left outer corner of the frame
53 * y - the y coordinate of the upper-left outer corner of the frame
54 * w - the width of the frame window w/o border
55 * h - the height of the frame window w/o border
56 * bw - the border width of the frame window or -1 not to change
57 *
58 * Special Considerations:
59 * This routine will check to make sure the window is not completely
60 * off the display, if it is, it'll bring some of it back on.
61 *
62 * The tmp_win->frame_XXX variables should NOT be updated with the
63 * values of x,y,w,h prior to calling this routine, since the new
64 * values are compared against the old to see whether a synthetic
65 * ConfigureNotify event should be sent. (It should be sent if the
66 * window was moved but not resized.)
67 *
68 ***********************************************************************
69 */
70void
71SetupWindow(TwmWindow *tmp_win, int x, int y, int w, int h, int bw)
72{
73 SetupFrame(tmp_win, x, y, w, h, bw, false);
74}
75
76void
77SetupFrame(TwmWindow *tmp_win, int x, int y, int w, int h, int bw,
78 bool sendEvent) /* whether or not to force a send */
79{
80 bool reShape;
81
82#ifdef DEBUG
83 fprintf(stderr, "SetupFrame: x=%d, y=%d, w=%d, h=%d, bw=%d\n",
84 x, y, w, h, bw);
85#endif
86
87 /* Negative border width is a magic value for "use current frame's" */
88 if(bw < 0) {
89 bw = tmp_win->frame_bw;
90 }
91
92
93 /*
94 * Set some bounds on the window location, to be sure part of it is
95 * visible.
96 */
97 {
98#define MARGIN (16 - 1) /* one "average" cursor width - 1 */
99 RArea area = RAreaNew(x, y, w, h);
100 int limit;
101
102 /* Make sure the window is not vertically off the screen */
103 limit = RLayoutFindBottomEdge(Scr->Layout, &area);
104 if(y > limit) {
105 y = limit - MARGIN;
106 }
107 else {
108 limit = RLayoutFindTopEdge(Scr->Layout, &area);
109 if(y + h + bw < limit) {
110 y = limit - h + MARGIN;
111 }
112 }
113
114 /* Make sure the window is not horizontally off the screen */
115 limit = RLayoutFindRightEdge(Scr->Layout, &area);
116 if(x > limit) {
117 x = limit - MARGIN;
118 }
119 else {
120 limit = RLayoutFindLeftEdge(Scr->Layout, &area);
121 if(x + w + bw < limit) {
122 x = limit - w + MARGIN;
123 }
124 }
125#undef MARGIN
126 }
127
128 /*
129 * Do some magic if the window being Setup'd is an icon manager. The
130 * width of an icon manager is variable, so something changing the
131 * width of the window needs to pass that info down to the control
132 * struct for the iconmgr. The height is solely determined by its
133 * contents though, so the h we're passed actually needs to be
134 * overridden based on how tall the iconmgr itself thinks it should
135 * be.
136 */
137 if(tmp_win->isiconmgr) {
138 tmp_win->iconmgrp->width = w - (2 * tmp_win->frame_bw3D);
139 h = tmp_win->iconmgrp->height + tmp_win->title_height +
140 (2 * tmp_win->frame_bw3D);
141 }
142
143 /*
144 * If the window is an Occupy window, we have to tell it about its
145 * new size too.
146 */
147 if(tmp_win->isoccupy) {
148 /* XXX maybe add something like ->iconmgrp above? */
149 OccupyWindow *occwin = Scr->workSpaceMgr.occupyWindow;
150
151 /* occwin not yet set during startup */
152 if(occwin != NULL && occwin->twm_win != NULL) {
153 if(tmp_win != occwin->twm_win) {
154 fprintf(stderr, "%s(): %p not the expected Occupy window %p.\n",
155 __func__, tmp_win, occwin->twm_win);
156 }
157 else {
159 }
160 }
161 }
162
163 /*
164 * According to the July 27, 1988 ICCCM draft, we should send a
165 * "synthetic" ConfigureNotify event to the client if the window
166 * was moved but not resized.
167 *
168 * In section "4.2.3 Window Move" in ICCCM 2.0. x-ref
169 * <https://tronche.com/gui/x/icccm/sec-4.html#s-4.2.3>
170 */
171 if(((x != tmp_win->frame_x || y != tmp_win->frame_y) &&
172 (w == tmp_win->frame_width && h == tmp_win->frame_height)) ||
173 (bw != tmp_win->frame_bw)) {
174 sendEvent = true;
175 }
176
177
178 /*
179 * Do the necessary sizing on the title window
180 */
181 {
183 unsigned int xwcm;
184 int title_width;
185
186 /* We're gonna be setting the width, even if it's unchanged */
187 xwcm = CWWidth;
188
189 /* Init: it's as wide as the window, minus borders */
190 title_width = xwc.width = w - (2 * tmp_win->frame_bw3D);
191
192 /*
193 * We really want to compute the offsets later, after the below
194 * block potentially changes title_width to deal with squeezing.
195 * However, adjusting and setting w->rightx based on the final
196 * 'squeeze' argument to CWTO() is what determines how far things
197 * get squeezed, so we need to call that first so the block can
198 * figure out the proper width to squeeze to.
199 *
200 * In the non-squeezing case, that arg does nothing, and we get
201 * all our values set. In the squeezing, though, all the values
202 * _but_ w->rightx get bogus values, so we'll have to call it
203 * again after we re-figure the width.
204 */
205 ComputeWindowTitleOffsets(tmp_win, title_width, true);
206
207 reShape = tmp_win->wShaped;
208
209 /*
210 * If the window has SqueezeTitle, the width of the titlebar may
211 * not be the width of the window (the w we're passed), so figure
212 * what it should be.
213 */
214 if(tmp_win->squeeze_info) {
215 title_width = tmp_win->rightx + Scr->TBInfo.rightoff;
216 if(title_width < xwc.width) {
217 xwc.width = title_width;
218 /*
219 * x-ref above comment. We set squeezed=false here so
220 * w->rightx gets figured right, because we're now
221 * passing the squeezed width. The remaining values are
222 * calculated the same, but will now be set right for the
223 * smaller size.
224 *
225 * See CWTO() comment for possible future cleanup.
226 */
227 ComputeWindowTitleOffsets(tmp_win, title_width, false);
228 if(tmp_win->frame_height != h ||
229 tmp_win->frame_width != w ||
230 tmp_win->frame_bw != bw ||
231 title_width != tmp_win->title_width) {
232 reShape = true;
233 }
234 }
235 else {
236 if(!tmp_win->wShaped) {
237 reShape = true;
238 }
239 title_width = xwc.width;
240 }
241 }
242
243 /* Write back whatever width we figured */
244 tmp_win->title_width = title_width;
245
246 /*
247 * If there is a titlebar, set the height.
248 *
249 * title_height=0 is a slightly stupid and nonintuitive way of
250 * flagging "we don't show a titlebar here", but what the heck...
251 */
252 if(tmp_win->title_height != 0) {
253 tmp_win->title_height = Scr->TitleHeight + bw;
254 }
255
256 /*
257 * If we've got a title window, XConfigure it.
258 *
259 * XXX Hang on, if we don't have a title window, all that work we
260 * just did was bogus, right? And in fact, doesn't accomplish
261 * much of anything anyway. Should this if() be around this
262 * whole block??
263 */
264 if(tmp_win->title_w) {
265 /* If border width is changing, update it and the X/Y too */
266 if(bw != tmp_win->frame_bw) {
267 xwc.border_width = bw;
268 tmp_win->title_x = xwc.x = tmp_win->frame_bw3D - bw;
269 tmp_win->title_y = xwc.y = tmp_win->frame_bw3D - bw;
270 xwcm |= (CWX | CWY | CWBorderWidth);
271 }
272
273 XConfigureWindow(dpy, tmp_win->title_w, xwcm, &xwc);
274 }
275 }
276
277
278 /*
279 * Set a few flags and values for the window as a whole
280 */
281 /* width/height changed? */
282 if(tmp_win->attr.width != w) {
283 tmp_win->widthEverChangedByUser = true;
284 }
285 if(tmp_win->attr.height != (h - tmp_win->title_height)) {
286 tmp_win->heightEverChangedByUser = true;
287 }
288
289 /* Write in new values, if the window isn't squeezed away */
290 if(!tmp_win->squeezed) {
291 tmp_win->attr.width = w - (2 * tmp_win->frame_bw3D);
292 tmp_win->attr.height = h - tmp_win->title_height - (2 * tmp_win->frame_bw3D);
293 }
294
295 /* If it is squeezed, stash values for when we unsqueeze */
296 if(tmp_win->squeezed) {
297 if(x != tmp_win->frame_x) {
298 tmp_win->actual_frame_x += x - tmp_win->frame_x;
299 }
300 if(y != tmp_win->frame_y) {
301 tmp_win->actual_frame_y += y - tmp_win->frame_y;
302 }
303 }
304
305
306 /*
307 * fix up frame and assign size/location values in tmp_win
308 */
309 {
311 unsigned int frame_mask;
312
313 frame_mask = 0;
314 if(bw != tmp_win->frame_bw) {
315 frame_wc.border_width = tmp_win->frame_bw = bw;
316 if(bw == 0) {
317 tmp_win->frame_bw3D = 0;
318 }
320 }
321 tmp_win->frame_x = x;
322 tmp_win->frame_y = y;
323 if(tmp_win->UnmapByMovingFarAway && !visible(tmp_win)) {
324 frame_wc.x = Scr->rootw + 1;
325 frame_wc.y = Scr->rooth + 1;
326 }
327 else {
328 frame_wc.x = tmp_win->frame_x;
329 frame_wc.y = tmp_win->frame_y;
330 }
331 frame_wc.width = tmp_win->frame_width = w;
332 frame_wc.height = tmp_win->frame_height = h;
333
334 /* Move/resize the frame */
335 frame_mask |= (CWX | CWY | CWWidth | CWHeight);
337
338 /*
339 * Move/resize the "real" window inside the frame. Is it
340 * actually meaningful to "move", since it's always the same
341 * place inside the frame? I'm not sure; this may be necessary
342 * for the client to re-learn its new position in the screen as a
343 * whole?
344 */
345 XMoveResizeWindow(dpy, tmp_win->w, tmp_win->frame_bw3D,
346 tmp_win->title_height + tmp_win->frame_bw3D,
347 tmp_win->attr.width, tmp_win->attr.height);
348 }
349
350
351 /*
352 * If there's a titlebar, we may have hilight/lolight windows in it
353 * to fix up.
354 *
355 * The sizing/positioning is all wonked up. In particular, the
356 * left-side hi/lolite windows don't work out right because they
357 * extend from the left side (after buttons) until name_x, which is
358 * the start of the title, which means they jam right up against the
359 * text. The math happens to mostly work out OK for UseThreeDTitles,
360 * but it doesn't do well in the opposing case.
361 *
362 * The right side never jam right up against the text, because their
363 * inside edge is highlightxr, figured in ComputeWindowTitleOffsets()
364 * to be name_x + name_width. Their placement is asymmetric with the
365 * above especially in the 2d case, but that may be a case of the R
366 * being wrong, not the L; x-ref discussion in CWTO() about it.
367 *
368 * It's probably necessary to fix both at once to get things coming
369 * out right. Of course, all the issues are invisible unless you're
370 * using TitleJustification center or right, which may be rare
371 * enough that nobody who cares enough has noticed...
372 */
373 if(tmp_win->title_height != 0) {
375 unsigned int xwcm;
376
377 /*
378 * Left-side window bits
379 */
380 /* Starts from highlightxl, goes to name_x */
381 xwc.width = (tmp_win->name_x - tmp_win->highlightxl);
382
383 /* Pad for 3d pop-in/out */
384 if(Scr->use3Dtitles) {
385 xwc.width -= Scr->TitleButtonShadowDepth;
386 }
387
388 /* Move offscreen if it's got no width to display, else place */
389 if(xwc.width <= 0) {
390 xwc.x = Scr->rootw; /* move offscreen */
391 xwc.width = 1;
392 }
393 else {
394 xwc.x = tmp_win->highlightxl;
395 }
396
397 /* We're setting the X placement and width */
398 xwcm = CWX | CWWidth;
399
400 /* Move it/them */
401 if(tmp_win->hilite_wl) {
402 XConfigureWindow(dpy, tmp_win->hilite_wl, xwcm, &xwc);
403 }
404 if(tmp_win->lolite_wl) {
405 XConfigureWindow(dpy, tmp_win->lolite_wl, xwcm, &xwc);
406 }
407
408
409 /*
410 * Right-side window bits
411 */
412 /* Full width is from the *lite window start to buttons start */
413 xwc.width = (tmp_win->rightx - tmp_win->highlightxr);
414
415 /* If there are buttons to our right, cut down for the padding */
416 if(Scr->TBInfo.nright > 0) {
417 xwc.width -= 2 * Scr->TitlePadding;
418 }
419
420 /* Rest is similar to above for left-side */
421 if(Scr->use3Dtitles) {
422 xwc.width -= Scr->TitleButtonShadowDepth;
423 }
424
425 /* xwc.width/x different from left, so can't just reuse the values */
426 if(xwc.width <= 0) {
427 xwc.x = Scr->rootw;
428 xwc.width = 1;
429 }
430 else {
431 xwc.x = tmp_win->highlightxr;
432 }
433
434 xwcm = CWX | CWWidth; // Not strictly necessary, same as above
435 if(tmp_win->hilite_wr) {
436 XConfigureWindow(dpy, tmp_win->hilite_wr, xwcm, &xwc);
437 }
438 if(tmp_win->lolite_wr) {
439 XConfigureWindow(dpy, tmp_win->lolite_wr, xwcm, &xwc);
440 }
441 }
442
443
444 /* Set X Shape stuff if we need to */
445 if(HasShape && reShape) {
447 }
448
449 /* Possible change how it looks in the WorkspaceManager */
450 WMapSetupWindow(tmp_win, x, y, w, h);
451
452 /*
453 * And send Configure notification to the (real) window if we decided
454 * we have to, telling it about what all has happened.
455 */
456 if(sendEvent) {
458
459 memset(&client_event, 0, sizeof(client_event)); // JIC
460
462 client_event.xconfigure.display = dpy;
463 client_event.xconfigure.event = tmp_win->w;
464 client_event.xconfigure.window = tmp_win->w;
465 client_event.xconfigure.x = (x + tmp_win->frame_bw - tmp_win->old_bw
466 + tmp_win->frame_bw3D);
467 client_event.xconfigure.y = (y + tmp_win->frame_bw +
468 tmp_win->title_height - tmp_win->old_bw
469 + tmp_win->frame_bw3D);
470 client_event.xconfigure.width = tmp_win->attr.width;
471 client_event.xconfigure.height = tmp_win->attr.height;
472 client_event.xconfigure.border_width = tmp_win->old_bw;
473 /* Real ConfigureNotify events say we're above title window, so ... */
474 /* what if we don't have a title ????? */
475 client_event.xconfigure.above = tmp_win->frame;
476 client_event.xconfigure.override_redirect = False;
478 }
479}
480
481
482/*
483 * Set X Shape extension bits for the window. This only gets called if
484 * we already know the server supports Shape, and if there's shaping to
485 * do. There's shaping to do if either the real window itself wants
486 * Shape'ing, or if we're SqueezeTitle'ing it.
487 */
488void
490{
491 /*
492 * See if the titlebar needs to move (relative to the frame). A
493 * common reason for this is using SqueezeTitle and squeezing the
494 * window as well; when the window is squeezed away, the titlebar is
495 * the only thing displayed, so the frame is coincident with it, and
496 * it starts at (0,0). But when the window is opened, and the
497 * titlebar is narrower than it, it starts at some x offset, so
498 * opening/closing the window squeeze needs to move the position
499 * relative to the frame.
500 */
501 if(tmp->title_w) {
502 int oldx = tmp->title_x, oldy = tmp->title_y;
504 if(oldx != tmp->title_x || oldy != tmp->title_y) {
505 XMoveWindow(dpy, tmp->title_w, tmp->title_x, tmp->title_y);
506 }
507 }
508
509 /*
510 * The frame consists of the shape of the contents window offset by
511 * title_height or'ed with the shape of title_w (which is always
512 * rectangular).
513 */
514 if(tmp->wShaped) {
515 /*
516 * need to do general case
517 */
519 tmp->frame_bw3D, tmp->title_height + tmp->frame_bw3D, tmp->w,
521 if(tmp->title_w) {
523 tmp->title_x + tmp->frame_bw,
524 tmp->title_y + tmp->frame_bw,
525 tmp->title_w, ShapeBounding,
526 ShapeUnion);
527 }
528 }
529 else {
530 /*
531 * The window itself isn't shaped, so we only need to handle
532 * shaping for what we're doing.
533 */
534 if(tmp->squeeze_info && !tmp->squeezed) {
535 /*
536 * Titlebar is squeezed and window is shown, so we need to
537 * shape out the missing bits on the side
538 * */
541 int fbw2 = 2 * tmp->frame_bw;
542
543 /*
544 * Build the border clipping rectangles; one around title, one
545 * around window. The title_[xy] field already have had frame_bw
546 * subtracted off them so that they line up properly in the frame.
547 *
548 * The frame_width and frame_height do *not* include borders.
549 */
550 /* border */
551 newBounding[0].x = tmp->title_x - tmp->frame_bw3D;
552 newBounding[0].y = tmp->title_y - tmp->frame_bw3D;
553 newBounding[0].width = tmp->title_width + fbw2 + 2 * tmp->frame_bw3D;
554 newBounding[0].height = tmp->title_height;
555 newBounding[1].x = -tmp->frame_bw;
556 newBounding[1].y = Scr->TitleHeight;
557 newBounding[1].width = tmp->attr.width + fbw2 + 2 * tmp->frame_bw3D;
558 newBounding[1].height = tmp->attr.height + fbw2 + 2 * tmp->frame_bw3D;
561 /* insides */
562 newClip[0].x = tmp->title_x + tmp->frame_bw - tmp->frame_bw3D;
563 newClip[0].y = 0;
564 newClip[0].width = tmp->title_width + 2 * tmp->frame_bw3D;
565 newClip[0].height = Scr->TitleHeight + tmp->frame_bw3D;
566 newClip[1].x = 0;
567 newClip[1].y = tmp->title_height;
568 newClip[1].width = tmp->attr.width + 2 * tmp->frame_bw3D;
569 newClip[1].height = tmp->attr.height + 2 * tmp->frame_bw3D;
572 }
573 else {
574 /*
575 * Full width title (or it's squeezed, but the window is also
576 * squeezed away, so it's the full width of what we're
577 * showing anyway), so our simple rectangle covers
578 * everything.
579 */
580 XShapeCombineMask(dpy, tmp->frame, ShapeBounding, 0, 0,
581 None, ShapeSet);
582 XShapeCombineMask(dpy, tmp->frame, ShapeClip, 0, 0,
583 None, ShapeSet);
584 }
585 }
586}
587
588
589
590/*
591 * Bits related to setting up titlebars. Their subwindows, icons,
592 * highlights, etc.
593 */
594
595/*
596 * ComputeTitleLocation - calculate the position of the title window; we need
597 * to take the frame_bw into account since we want (0,0) of the title window
598 * to line up with (0,0) of the frame window.
599 *
600 * This sets ->title_[xy], which are the (x,y) of the ->title_w relative
601 * to the frame window.
602 */
603void
605{
606 /* y position is always the same */
607 tmp->title_y = tmp->frame_bw3D - tmp->frame_bw;
608
609 /* x can vary depending on squeezing */
610 if(tmp->squeeze_info && !tmp->squeezed) {
611 SqueezeInfo *si = tmp->squeeze_info;
612 int basex;
613 int maxwidth = tmp->frame_width;
614 int tw = tmp->title_width + 2 * tmp->frame_bw3D;
615
616 /* figure label base from squeeze info (justification fraction) */
617 if(si->denom == 0) { /* num is pixel based */
618 basex = si->num;
619 }
620 else { /* num/denom is fraction */
621 basex = ((si->num * maxwidth) / si->denom);
622 }
623 if(si->num < 0) {
624 basex += maxwidth;
625 }
626
627 /* adjust for left (nop), center, right justify */
628 switch(si->justify) {
629 case SIJ_LEFT:
630 break; // nop
631 case SIJ_CENTER:
632 basex -= tw / 2;
633 break;
634 case SIJ_RIGHT:
635 basex -= tw - 1;
636 break;
637 }
638
639 /* Clip */
640 if(basex > maxwidth - tw) {
641 basex = maxwidth - tw;
642 }
643 if(basex < 0) {
644 basex = 0;
645 }
646
647 tmp->title_x = basex - tmp->frame_bw + tmp->frame_bw3D;
648 }
649 else {
650 tmp->title_x = tmp->frame_bw3D - tmp->frame_bw;
651 }
652}
653
654
655/*
656 * Despite being called "TitlebarButtons", this actually sets up most of
657 * the subwindows inside the titlebar. There are windows for each
658 * button, but also windows for the shifting-color regions on un/focus.
659 */
660void
662{
663 unsigned long valuemask; /* mask for create windows */
664 XSetWindowAttributes attributes; /* attributes for create windows */
665 int leftx, rightx, y;
667 int nb;
668
669 /*
670 * If there's no titlebar, we don't need any subwindows or anything,
671 * so just make sure it's all empty and return.
672 */
673 if(tmp_win->title_height == 0) {
674 tmp_win->hilite_wl = (Window) 0;
675 tmp_win->hilite_wr = (Window) 0;
676 tmp_win->lolite_wl = (Window) 0;
677 tmp_win->lolite_wr = (Window) 0;
678 return;
679 }
680
681
682 /* Figure where things go */
683 ComputeWindowTitleOffsets(tmp_win, tmp_win->attr.width, false);
684
685 leftx = y = Scr->TBInfo.leftx;
686 rightx = tmp_win->rightx;
687
688 /*
689 * Setup default attributes for creating the subwindows for each
690 * button.
691 */
692 attributes.win_gravity = NorthWestGravity;
693 attributes.background_pixel = tmp_win->title.back;
694 attributes.border_pixel = tmp_win->title.fore;
697 attributes.cursor = Scr->ButtonCursor;
699 CWCursor);
700
701 /*
702 * Initialize the button images/subwindows for the left/right.
703 */
704 tmp_win->titlebuttons = NULL;
705 nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;
706 if(nb > 0) {
707 /*
708 * XXX Rework this into a proper array, either NULL-terminated or
709 * with a stored size, instead of manually implementing a re-calc
710 * of the size and incrementing pointers every time we want to
711 * walk this.
712 */
713 tmp_win->titlebuttons = calloc(nb, sizeof(TBWindow));
714 if(!tmp_win->titlebuttons) {
715 fprintf(stderr, "%s: unable to allocate %d titlebuttons\n",
716 ProgramName, nb);
717 }
718 else {
719 TBWindow *tbw;
720 int boxwidth = (Scr->TBInfo.width + Scr->TBInfo.pad);
721 unsigned int h = (Scr->TBInfo.width - Scr->TBInfo.border * 2);
722
723 for(tb = Scr->TBInfo.head, tbw = tmp_win->titlebuttons; tb;
724 tb = tb->next, tbw++) {
725 int x;
726 if(tb->rightside) {
727 x = rightx;
728 rightx += boxwidth;
729 attributes.win_gravity = NorthEastGravity;
730 }
731 else {
732 x = leftx;
733 leftx += boxwidth;
734 attributes.win_gravity = NorthWestGravity;
735 }
736 tbw->window = XCreateWindow(dpy, tmp_win->title_w, x, y, h, h,
737 Scr->TBInfo.border,
741 if(Scr->NameDecorations) {
742 XStoreName(dpy, tbw->window, "TB button");
743 }
744
745 /*
746 * XXX Can we just use tb->image for this instead? I
747 * think we can. The TBInfo.head list is assembled in
748 * calls to CreateTitleButton(), which happen during
749 * config file parsing, and then during
750 * InitTitlebarButtons(), which then goes through and
751 * tb->image = GetImage()'s each of the entries. I don't
752 * believe anything ever gets added to TBInfo.head after
753 * that. And the setting in ITB() could only fail in
754 * cases that would presumably also fail for us here. So
755 * this whole block is redundant?
756 */
757 tbw->image = GetImage(tb->name, tmp_win->title);
758 if(! tbw->image) {
759 tbw->image = GetImage(TBPM_QUESTION, tmp_win->title);
760 if(! tbw->image) { /* cannot happen (see util.c) */
761 fprintf(stderr, "%s: unable to add titlebar button \"%s\"\n",
762 ProgramName, tb->name);
763 }
764 }
765 tbw->info = tb;
766 }
767 }
768 }
769
770 /* Windows in the titlebar that show focus */
773
774 /* Map all those windows we just created... */
775 XMapSubwindows(dpy, tmp_win->title_w);
776
777 /*
778 * ...but hide away the hilite's, since they'll only show up when we
779 * give the window focus. And when we do (even if that when is
780 * "right now"), the focus handler will handle mapping them for us.
781 */
782 if(tmp_win->hilite_wl) {
783 XUnmapWindow(dpy, tmp_win->hilite_wl);
784 }
785 if(tmp_win->hilite_wr) {
786 XUnmapWindow(dpy, tmp_win->hilite_wr);
787 }
788
789 /*
790 * ... but DO show the lolite's, because... XXX this shouldn't be
791 * necessary at all, because they would already have been mapped
792 * during the XMapSubwindows() call above?
793 */
794 if(tmp_win->lolite_wl) {
795 XMapWindow(dpy, tmp_win->lolite_wl);
796 }
797 if(tmp_win->lolite_wr) {
798 XMapWindow(dpy, tmp_win->lolite_wr);
799 }
800
801 return;
802}
803
804
805/*
806 * Figure out where the window title and the hi/lolite windows go within
807 * the titlebar as a whole.
808 *
809 * For a particular window, called during the AddWindow() process, and
810 * also via Setup{Window,Frame}().
811 *
812 * This sets w->name_x (x offset for writing the name), w->highlightx[lr]
813 * (x offset for left/right hilite windows), and w->rightx (x offset for
814 * the right buttons), all relative to the title window.
815 *
816 *
817 * The 'squeeze' argument controls whether rightoff should be corrected
818 * for squeezing; when true, it means the passed width doesn't take into
819 * account squeezing. In fact, this adjustment of rightx is what winds
820 * up determining how small the bar gets squeezed to. This relates to
821 * why it's called twice in SetupFrame() to set things up right.
822 *
823 * XXX Should probably either rework how the squeezed width is figured,
824 * or use squeeze to correct everything in here to reduce the scary magic
825 * double-calling.
826 */
827static void
829{
830 /*
831 * Space available for the window title for calculating name_x.
832 * (window width) - (space reserved l and r for buttons)
833 */
834 const int titlew = width - Scr->TBInfo.titlex - Scr->TBInfo.rightoff;
835
836 /*
837 * If our title is long enough, it'll overflow the available space.
838 * At that point, any "justification" is pretty moot, so just pretend
839 * anything long enough is left-justified.
840 */
841 const TitleJust eff_just = (tmp_win->name_width >= titlew)
842 ? TJ_LEFT : Scr->TitleJustification;
843
844 /*
845 * First figure where the window name goes, depending on
846 * TitleJustification. If it's on the left/right, and we're using 3d
847 * titles, we have to move it past the TitleShadowDepth, plus a
848 * little extra for visual padding.
849 *
850 * If it's in the middle, we just center on the middle of the
851 * name, without taking into account what that will do if the name is
852 * "too long" for our space, which causes really bad side effects.
853 * The fixing below at least theoretically fixes that, though other
854 * parts of the drawing will still cause Bad Side Effects.
855 */
856 switch(eff_just) {
857 case TJ_UNDEF:
858 /* Can't happen; fallthru to TJ_LEFT */
859 fprintf(stderr, "%s(): Unexpected Scr->TitleJustification %d, "
860 "treating as left\n", __func__, Scr->TitleJustification);
861 case TJ_LEFT:
862 tmp_win->name_x = Scr->TBInfo.titlex;
863 if(Scr->use3Dtitles) {
864 tmp_win->name_x += Scr->TitleShadowDepth + 2;
865 }
866 break;
867 case TJ_CENTER:
868 tmp_win->name_x = Scr->TBInfo.titlex + (titlew - tmp_win->name_width) / 2;
869 break;
870 case TJ_RIGHT:
871 /*
872 * XXX Since this pushes the end of the name way over to the
873 * right, there's no room for the right highlight window.
874 * But shrinking down the size of that is how the titlebar
875 * gets squeezed for SqueezeTitle. So if TJ_RIGHT, the
876 * titlebar will never get squeezed.
877 */
878 tmp_win->name_x = Scr->TBInfo.titlex + titlew - tmp_win->name_width;
879 if(Scr->use3Dtitles) {
880 tmp_win->name_x -= Scr->TitleShadowDepth - 2;
881 }
882 break;
883 }
884
885 /*
886 * Adjust for sanity. Make sure it's always no earlier than the
887 * start of the titlebar (possible especially in the TJ_CENTER case,
888 * but also theoretically if you set a negative ShadowDepth, which
889 * would be stupid and might break other stuff). In the 3d case,
890 * allow twice the ShadowDepth (once for the shadow itself, the
891 * second time for visual padding).
892 */
893 if(Scr->use3Dtitles) {
894 if(tmp_win->name_x < (Scr->TBInfo.titlex + 2 * Scr->TitleShadowDepth)) {
895 tmp_win->name_x = Scr->TBInfo.titlex + 2 * Scr->TitleShadowDepth;
896 }
897 }
898 else if(tmp_win->name_x < Scr->TBInfo.titlex) {
899 tmp_win->name_x = Scr->TBInfo.titlex;
900 }
901
902
903 /*
904 * Left hilite window starts at the left side, plus some space for a
905 * shadow for 3d effects. That's easy.
906 */
907 tmp_win->highlightxl = Scr->TBInfo.titlex;
908 if(Scr->use3Dtitles) {
909 tmp_win->highlightxl += Scr->TitleShadowDepth;
910 }
911
912 /*
913 * Right hilite window starts after the window name.
914 *
915 * With ThreeDTitles, add +2 to match the spacing added onto the left
916 * size of name_x above.
917 *
918 * If there's a window to show for the hilite, and there are buttons
919 * for the right side, we move it over even further. This
920 * particularly causes extra blank space between the name and hilite
921 * bar in the !(UseThreeDTitles) case (because TitlePadding is >0 by
922 * default there). I'm not sure why this is here. I seem to get
923 * better results in both 3D/!3D cases by unconditionally doing the
924 * +=2, and never adding the TitlePadding. Perhaps it should be
925 * changed?
926 */
927 tmp_win->highlightxr = tmp_win->name_x + tmp_win->name_width;
928 if(Scr->use3Dtitles) {
929 tmp_win->highlightxr += 2;
930 }
931 if(tmp_win->hilite_wr || Scr->TBInfo.nright > 0) {
932 tmp_win->highlightxr += Scr->TitlePadding;
933 }
934
935
936 /*
937 * rightoff tells us how much space we need on the right for the
938 * buttons, a little math with the width tells us how far in from the
939 * left to start for that.
940 *
941 * However, if the title bar is squeezed and the window's up, the
942 * titlebar width will be smaller than our 'width' var (which
943 * describes the window as a whole), so we have to make sure it can't
944 * be too far. So start where the right hilite window goes, with a
945 * little space for it to show up, plus misc padding. x-ref comment
946 * at top of function about the weird ways this gets used.
947 */
948 tmp_win->rightx = width - Scr->TBInfo.rightoff;
949 if(squeeze && tmp_win->squeeze_info && !tmp_win->squeezed) {
950 int rx = (tmp_win->highlightxr
951 + (tmp_win->hilite_wr ? Scr->TBInfo.width * 2 : 0)
952 + (Scr->TBInfo.nright > 0 ? Scr->TitlePadding : 0)
953 + Scr->FramePadding);
954 if(rx < tmp_win->rightx) {
955 tmp_win->rightx = rx;
956 }
957 }
958 return;
959}
960
961
962/*
963 * Creation/destruction of "hi/lolite windows". These are the
964 * portion[s] of the title bar which change color/form to indicate focus.
965 */
966static void
968{
969 XSetWindowAttributes attributes; /* attributes for create windows */
970 unsigned long valuemask;
971 int h = (Scr->TitleHeight - 2 * Scr->FramePadding);
972 int y = Scr->FramePadding;
973
974 /* Init */
975 tmp_win->hilite_wl = (Window) 0;
976 tmp_win->hilite_wr = (Window) 0;
977
978 /* If this window has NoTitleHighlight, don't do nuthin' */
979 if(! tmp_win->titlehighlight) {
980 return;
981 }
982
983 /*
984 * If a special highlight pixmap was given, use that. Otherwise,
985 * use a nice, even gray pattern. The old horizontal lines look really
986 * awful on interlaced monitors (as well as resembling other looks a
987 * little bit too closely), but can be used by putting
988 *
989 * Pixmaps { TitleHighlight "hline2" }
990 *
991 * (or whatever the horizontal line bitmap is named) in the startup
992 * file. If all else fails, use the foreground color to look like a
993 * solid line.
994 */
995 if(! tmp_win->HiliteImage) {
996 if(Scr->HighlightPixmapName) {
997 tmp_win->HiliteImage = GetImage(Scr->HighlightPixmapName, tmp_win->title);
998 }
999 }
1000 if(! tmp_win->HiliteImage) {
1001 /* No defined image, create shaded bars */
1002 Pixmap pm;
1003 char *which;
1004
1005 if(Scr->use3Dtitles && (Scr->Monochrome != COLOR)) {
1006 which = "black";
1007 }
1008 else {
1009 which = "gray";
1010 }
1011
1012 pm = mk_blackgray_pixmap(which, tmp_win->title_w,
1013 tmp_win->title.fore, tmp_win->title.back);
1014
1015 tmp_win->HiliteImage = AllocImage();
1016 tmp_win->HiliteImage->pixmap = pm;
1017 get_blackgray_size(&(tmp_win->HiliteImage->width),
1018 &(tmp_win->HiliteImage->height));
1019 }
1020
1021 /* Use what we came up with, or fall back to solid pixels */
1022 if(tmp_win->HiliteImage) {
1024 attributes.background_pixmap = tmp_win->HiliteImage->pixmap;
1025 }
1026 else {
1028 attributes.background_pixel = tmp_win->title.fore;
1029 }
1030
1031 /*
1032 * Adjust y-positioning and height for 3d extras. Both are fixed
1033 * from the time the titlebar is created. The X position gets
1034 * changed on any sort of resize etc, and SetupFrame() handles that.
1035 * We just left 'em at X position 0 here, they'll get moved by SF()
1036 * before being displayed anyway.
1037 */
1038 if(Scr->use3Dtitles) {
1039 y += Scr->TitleShadowDepth;
1040 h -= 2 * Scr->TitleShadowDepth;
1041 }
1042
1043 /*
1044 * There's a left hilite window unless the title is flush left, and
1045 * similarly for the right.
1046 */
1047#define MKWIN() XCreateWindow(dpy, tmp_win->title_w, 0, y, \
1048 Scr->TBInfo.width, h, \
1049 0, Scr->d_depth, CopyFromParent, \
1050 Scr->d_visual, valuemask, &attributes)
1051 if(Scr->TitleJustification != TJ_LEFT) {
1052 tmp_win->hilite_wl = MKWIN();
1053 if(Scr->NameDecorations) {
1054 XStoreName(dpy, tmp_win->hilite_wl, "hilite_wl");
1055 }
1056 }
1057 if(Scr->TitleJustification != TJ_RIGHT) {
1058 tmp_win->hilite_wr = MKWIN();
1059 if(Scr->NameDecorations) {
1060 XStoreName(dpy, tmp_win->hilite_wr, "hilite_wr");
1061 }
1062 }
1063#undef MKWIN
1064}
1065
1066
1067/*
1068 * Used in events.c in HandleDestroyNotify(), not here. Called during
1069 * window destruction. Technically, this isn't actually deleting the
1070 * windows; the XDestroyWindow() call it makes will destroy all the
1071 * sub-windows. This is actually just for freeing the image we put in
1072 * the window, if there is one.
1073 */
1074void
1076{
1077 if(tmp_win->HiliteImage) {
1078 if(Scr->HighlightPixmapName) {
1079 /*
1080 * Image obtained from GetImage(): it is in a cache
1081 * so we don't need to free it. There will not be multiple
1082 * copies if the same xpm:foo image is requested again.
1083 */
1084 }
1085 else {
1086 XFreePixmap(dpy, tmp_win->HiliteImage->pixmap);
1087 free(tmp_win->HiliteImage);
1088 }
1089 tmp_win->HiliteImage = NULL;
1090 }
1091}
1092
1093
1094static void
1096{
1097 XSetWindowAttributes attributes; /* attributes for create windows */
1098 unsigned long valuemask;
1099 int h = (Scr->TitleHeight - 2 * Scr->FramePadding);
1100 int y = Scr->FramePadding;
1101 ColorPair cp;
1102
1103 /* Init */
1104 tmp_win->lolite_wl = (Window) 0;
1105 tmp_win->lolite_wr = (Window) 0;
1106
1107 /*
1108 * We don't even make lolite windows unless UseSunkTitlePixmap is
1109 * set.
1110 */
1111 if(!Scr->UseSunkTitlePixmap || ! tmp_win->titlehighlight) {
1112 return;
1113 }
1114
1115 /*
1116 * If there's a defined pixmap for highlights, use that with some
1117 * flipped colors.
1118 * */
1119 if(! tmp_win->LoliteImage) {
1120 if(Scr->HighlightPixmapName) {
1121 cp = tmp_win->title;
1122 cp.shadc = tmp_win->title.shadd;
1123 cp.shadd = tmp_win->title.shadc;
1124 tmp_win->LoliteImage = GetImage(Scr->HighlightPixmapName, cp);
1125 }
1126 }
1127
1128 /* Use our image, or fall back to solid colored bar */
1129 if(tmp_win->LoliteImage) {
1131 attributes.background_pixmap = tmp_win->LoliteImage->pixmap;
1132 }
1133 else {
1135 attributes.background_pixel = tmp_win->title.fore;
1136 }
1137
1138 /* Extra padding for 3d decorations */
1139 if(Scr->use3Dtitles) {
1140 y += Scr->TitleShadowDepth;
1141 h -= 2 * Scr->TitleShadowDepth;
1142 }
1143
1144 /*
1145 * Bar on the left, unless the title is flush left, and ditto right.
1146 * Same invocation as above for hilites.
1147 */
1148#define MKWIN() XCreateWindow(dpy, tmp_win->title_w, 0, y, \
1149 Scr->TBInfo.width, h, \
1150 0, Scr->d_depth, CopyFromParent, \
1151 Scr->d_visual, valuemask, &attributes)
1152 if(Scr->TitleJustification != TJ_LEFT) {
1153 tmp_win->lolite_wl = MKWIN();
1154 if(Scr->NameDecorations) {
1155 XStoreName(dpy, tmp_win->lolite_wl, "lolite_wl");
1156 }
1157 }
1158 if(Scr->TitleJustification != TJ_RIGHT) {
1159 tmp_win->lolite_wr = MKWIN();
1160 if(Scr->NameDecorations) {
1161 XStoreName(dpy, tmp_win->lolite_wr, "lolite_wr");
1162 }
1163 }
1164#undef MKWIN
1165}
1166
1167/*
1168 * There is no DeleteLowlightWindows() as a counterpart to the
1169 * HighlightWindows variant. That func doesn't delete the [sub-]window;
1170 * that happens semi-automatically when the frame window is destroyed.
1171 * It only cleans up the Pixmap if there is one. And the only way the
1172 * Lowlight window can wind up with a pixmap is as a copy of the
1173 * highlight window one, in which case when THAT delete gets called all
1174 * the cleanup is done.
1175 */
1176
1177
1178
1179
1180/*
1181 * Painting the titlebars. The actual displaying of the stuff that's
1182 * figured or stored above.
1183 */
1184
1185/*
1186 * Write in the window title
1187 */
1188void
1190{
1191 /* Draw 3d border around title bits */
1192 if(Scr->use3Dtitles) {
1193 /*
1194 * From the start of the title bits (after left button), to the
1195 * start of the right buttons, minus padding.
1196 */
1197 int wid = tmp_win->title_width - Scr->TBInfo.titlex
1198 - Scr->TBInfo.rightoff - Scr->TitlePadding;
1199 ButtonState state = off;
1200
1201 /*
1202 * If SunkFocusWindowTitle, then we "sink in" the whole title
1203 * window when it's focused. Otherwise (!SunkFocus || !focused)
1204 * it's popped up.
1205 */
1206 if(Scr->SunkFocusWindowTitle && (Scr->Focus == tmp_win) &&
1207 (tmp_win->title_height != 0)) {
1208 state = on;
1209 }
1210
1211 Draw3DBorder(tmp_win->title_w, Scr->TBInfo.titlex, 0, wid,
1212 Scr->TitleHeight, Scr->TitleShadowDepth,
1213 tmp_win->title, state, true, false);
1214 }
1215
1216 /* Setup the X graphics context for the drawing */
1217 FB(tmp_win->title.fore, tmp_win->title.back);
1218
1219 /* And write in the name */
1220 if(Scr->use3Dtitles) {
1221 int width, mwidth, len;
1224
1225 /*
1226 * Do a bunch of trying to chop the length down until it will fit
1227 * into the space. This doesn't seem to actually accomplish
1228 * anything at the moment, as somehow we wind up with nothing
1229 * visible in the case of a long enough title.
1230 */
1231 len = strlen(tmp_win->name);
1232 XmbTextExtents(Scr->TitleBarFont.font_set,
1233 tmp_win->name, len,
1235 width = logical_rect.width;
1236 mwidth = tmp_win->title_width - Scr->TBInfo.titlex -
1237 Scr->TBInfo.rightoff - Scr->TitlePadding -
1238 Scr->TitleShadowDepth - 4;
1239 while((len > 0) && (width > mwidth)) {
1240 len--;
1241 XmbTextExtents(Scr->TitleBarFont.font_set,
1242 tmp_win->name, len,
1244 width = logical_rect.width;
1245 }
1246
1247 /*
1248 * Write it in. The Y position is subtly different from the
1249 * !3Dtitles case due to the potential bordering around it. It's
1250 * not quite clear whether it should be.
1251 */
1252 ((Scr->Monochrome != COLOR) ? XmbDrawImageString : XmbDrawString)
1253 (dpy, tmp_win->title_w, Scr->TitleBarFont.font_set,
1254 Scr->NormalGC,
1255 tmp_win->name_x,
1256 (Scr->TitleHeight - logical_rect.height) / 2 + (- logical_rect.y),
1257 tmp_win->name, len);
1258 }
1259 else {
1260 /*
1261 * XXX The 3Dtitle case above has attempted correction for a lot of
1262 * stuff. It's not entirely clear that it's either needed there,
1263 * or not needed here. It's also not obvious that the magic
1264 * it does to support monochrome isn't applicable here, thought
1265 * it may be a side effect of differences in how the backing
1266 * titlebar is painted. This requires investigation, and either
1267 * fixing the wrong or documentation of why it's right.
1268 */
1269 XmbDrawString(dpy, tmp_win->title_w, Scr->TitleBarFont.font_set,
1270 Scr->NormalGC,
1271 tmp_win->name_x, Scr->TitleBarFont.y,
1272 tmp_win->name, strlen(tmp_win->name));
1273 }
1274}
1275
1276
1277/*
1278 * Painting in the buttons on the titlebar
1279 */
1280/* Iterate and show them all */
1281void
1283{
1284 int i;
1285 TBWindow *tbw;
1286 int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;
1287
1288 for(i = 0, tbw = tmp_win->titlebuttons; i < nb; i++, tbw++) {
1289 if(tbw) {
1291 }
1292 }
1293}
1294
1295/* Blit the pixmap into the right place */
1296void
1298{
1299 TitleButton *tb = tbw->info;
1300
1301 XCopyArea(dpy, tbw->image->pixmap, tbw->window, Scr->NormalGC,
1302 tb->srcx, tb->srcy, tb->width, tb->height,
1303 tb->dstx, tb->dsty);
1304 return;
1305}
1306
1307
1308
1309
1310/*
1311 * Stuff for window borders
1312 */
1313
1314
1315/*
1316 * Currently only used in drawing window decoration borders. Contrast
1317 * with Draw3DBorder() which is used for all sorts of generalized
1318 * drawing.
1319 */
1320static void
1321Draw3DCorner(Window w, int x, int y, int width, int height,
1322 int thick, int bw, ColorPair cp, CornerType type)
1323{
1324 XRectangle rects [2];
1325
1326 switch(type) {
1327 case TopLeft:
1328 Draw3DBorder(w, x, y, width, height, bw, cp, off, true, false);
1329 Draw3DBorder(w, x + thick - bw, y + thick - bw,
1330 width - thick + 2 * bw, height - thick + 2 * bw,
1331 bw, cp, on, true, false);
1332 break;
1333 case TopRight:
1334 Draw3DBorder(w, x, y, width, height, bw, cp, off, true, false);
1335 Draw3DBorder(w, x, y + thick - bw,
1336 width - thick + bw, height - thick,
1337 bw, cp, on, true, false);
1338 break;
1339 case BottomRight:
1340 rects [0].x = x + width - thick;
1341 rects [0].y = y;
1342 rects [0].width = thick;
1343 rects [0].height = height;
1344 rects [1].x = x;
1345 rects [1].y = y + width - thick;
1346 rects [1].width = width - thick;
1347 rects [1].height = thick;
1348 XSetClipRectangles(dpy, Scr->BorderGC, 0, 0, rects, 2, Unsorted);
1349 Draw3DBorder(w, x, y, width, height, bw, cp, off, true, false);
1350 Draw3DBorder(w, x, y,
1351 width - thick + bw, height - thick + bw,
1352 bw, cp, on, true, false);
1353 XSetClipMask(dpy, Scr->BorderGC, None);
1354 break;
1355 case BottomLeft:
1356 rects [0].x = x;
1357 rects [0].y = y;
1358 rects [0].width = thick;
1359 rects [0].height = height;
1360 rects [1].x = x + thick;
1361 rects [1].y = y + height - thick;
1362 rects [1].width = width - thick;
1363 rects [1].height = thick;
1364 XSetClipRectangles(dpy, Scr->BorderGC, 0, 0, rects, 2, Unsorted);
1365 Draw3DBorder(w, x, y, width, height, bw, cp, off, true, false);
1366 Draw3DBorder(w, x + thick - bw, y,
1367 width - thick, height - thick + bw,
1368 bw, cp, on, true, false);
1369 XSetClipMask(dpy, Scr->BorderGC, None);
1370 break;
1371 default:
1372 /* Bad code */
1373 fprintf(stderr, "Internal error: Invalid Draw3DCorner type %d\n",
1374 type);
1375 break;
1376 }
1377 return;
1378}
1379
1380
1381/*
1382 * Draw the borders onto the frame for a window
1383 */
1384void
1386{
1387 ColorPair cp;
1388
1389 /* Set coloring based on focus/highlight state */
1390 cp = (focus && tmp_win->highlight) ? tmp_win->borderC : tmp_win->border_tile;
1391
1392 /*
1393 * If there's no height to the title bar (e.g., on NoTitle windows),
1394 * there's nothing much to corner around, so we can just border up
1395 * the whole thing. Since the bordering on the frame is "below" the
1396 * real window, we can just draw one giant square, and then one
1397 * slightly smaller (but still larger than the real-window itself)
1398 * square on top of it, and voila; border!
1399 */
1400 if(tmp_win->title_height == 0) {
1401 Draw3DBorder(tmp_win->frame, 0, 0,
1402 tmp_win->frame_width, tmp_win->frame_height,
1403 Scr->BorderShadowDepth, cp, off, true, false);
1404 Draw3DBorder(tmp_win->frame,
1405 tmp_win->frame_bw3D - Scr->BorderShadowDepth,
1406 tmp_win->frame_bw3D - Scr->BorderShadowDepth,
1407 tmp_win->frame_width - 2 * tmp_win->frame_bw3D + 2 * Scr->BorderShadowDepth,
1408 tmp_win->frame_height - 2 * tmp_win->frame_bw3D + 2 * Scr->BorderShadowDepth,
1409 Scr->BorderShadowDepth, cp, on, true, false);
1410 return;
1411 }
1412
1413 /*
1414 * Otherwise, we have to draw corners, which means we have to
1415 * individually draw the 4 side borders between them as well.
1416 *
1417 * So start by laying out the 4 corners.
1418 */
1419
1420 /* How far the corners extend along the sides */
1421#define CORNERLEN (Scr->TitleHeight + tmp_win->frame_bw3D)
1422
1423 Draw3DCorner(tmp_win->frame,
1424 tmp_win->title_x - tmp_win->frame_bw3D,
1425 0,
1427 tmp_win->frame_bw3D, Scr->BorderShadowDepth, cp, TopLeft);
1428 Draw3DCorner(tmp_win->frame,
1429 tmp_win->title_x + tmp_win->title_width - Scr->TitleHeight,
1430 0,
1432 tmp_win->frame_bw3D, Scr->BorderShadowDepth, cp, TopRight);
1433 Draw3DCorner(tmp_win->frame,
1434 tmp_win->frame_width - CORNERLEN,
1435 tmp_win->frame_height - CORNERLEN,
1437 tmp_win->frame_bw3D, Scr->BorderShadowDepth, cp, BottomRight);
1438 Draw3DCorner(tmp_win->frame,
1439 0,
1440 tmp_win->frame_height - CORNERLEN,
1442 tmp_win->frame_bw3D, Scr->BorderShadowDepth, cp, BottomLeft);
1443
1444
1445 /*
1446 * And draw the borders on the 4 sides between the corners
1447 */
1448 /* Top */
1449 Draw3DBorder(tmp_win->frame,
1450 tmp_win->title_x + Scr->TitleHeight,
1451 0,
1452 tmp_win->title_width - 2 * Scr->TitleHeight,
1453 tmp_win->frame_bw3D,
1454 Scr->BorderShadowDepth, cp, off, true, false);
1455 /* Bottom */
1456 Draw3DBorder(tmp_win->frame,
1457 tmp_win->frame_bw3D + Scr->TitleHeight,
1458 tmp_win->frame_height - tmp_win->frame_bw3D,
1459 tmp_win->frame_width - 2 * CORNERLEN,
1460 tmp_win->frame_bw3D,
1461 Scr->BorderShadowDepth, cp, off, true, false);
1462 /* Left */
1463 Draw3DBorder(tmp_win->frame,
1464 0,
1465 Scr->TitleHeight + tmp_win->frame_bw3D,
1466 tmp_win->frame_bw3D,
1467 tmp_win->frame_height - 2 * CORNERLEN,
1468 Scr->BorderShadowDepth, cp, off, true, false);
1469 /* Right */
1470 Draw3DBorder(tmp_win->frame,
1471 tmp_win->frame_width - tmp_win->frame_bw3D,
1472 Scr->TitleHeight + tmp_win->frame_bw3D,
1473 tmp_win->frame_bw3D,
1474 tmp_win->frame_height - 2 * CORNERLEN,
1475 Scr->BorderShadowDepth, cp, off, true, false);
1476
1477#undef CORNERLEN
1478
1479
1480 /*
1481 * If SqueezeTitle is set for the window, and the window isn't
1482 * squeezed away (whether because it's focused, or it's just not
1483 * squeezed at all), then we need to draw a "top" border onto the
1484 * bare bits of the window to the left/right of where the titlebar
1485 * is.
1486 */
1487 if(tmp_win->squeeze_info && !tmp_win->squeezed) {
1488 /* To the left */
1489 Draw3DBorder(tmp_win->frame,
1490 0,
1491 Scr->TitleHeight,
1492 tmp_win->title_x,
1493 tmp_win->frame_bw3D,
1494 Scr->BorderShadowDepth, cp, off, true, false);
1495 /* And the right */
1496 Draw3DBorder(tmp_win->frame,
1497 tmp_win->title_x + tmp_win->title_width,
1498 Scr->TitleHeight,
1499 tmp_win->frame_width - tmp_win->title_x - tmp_win->title_width,
1500 tmp_win->frame_bw3D,
1501 Scr->BorderShadowDepth, cp, off, true, false);
1502 }
1503}
1504
1505
1506/*
1507 * Setup the mouse cursor for various locations on the border of a
1508 * window.
1509 *
1510 * Formerly in util.c
1511 */
1512void
1514{
1515 Cursor cursor;
1517 int h, fw, fh, wd;
1518
1519 if(!tmp_win) {
1520 return;
1521 }
1522
1523 /* Use the max of these, but since one is always 0 we can add them. */
1524 wd = tmp_win->frame_bw + tmp_win->frame_bw3D;
1525 h = Scr->TitleHeight + wd;
1526 fw = tmp_win->frame_width;
1527 fh = tmp_win->frame_height;
1528
1529#if defined DEBUG && DEBUG
1530 fprintf(stderr, "wd=%d h=%d, fw=%d fh=%d x=%d y=%d\n",
1531 wd, h, fw, fh, x, y);
1532#endif
1533
1534 /*
1535 * If not using 3D borders:
1536 *
1537 * The left border has negative x coordinates,
1538 * The top border (above the title) has negative y coordinates.
1539 * The title is TitleHeight high, the next wd pixels are border.
1540 * The bottom border has coordinates >= the frame height.
1541 * The right border has coordinates >= the frame width.
1542 *
1543 * If using 3D borders: all coordinates are >= 0, and all coordinates
1544 * are higher by the border width.
1545 *
1546 * Since we only get events when we're actually in the border, we simply
1547 * allow for both cases at the same time.
1548 */
1549
1550 if((x < -wd) || (y < -wd)) {
1551 cursor = Scr->FrameCursor;
1552 }
1553 else if(x < h) {
1554 if(y < h) {
1555 cursor = TopLeftCursor;
1556 }
1557 else if(y >= fh - h) {
1558 cursor = BottomLeftCursor;
1559 }
1560 else {
1561 cursor = LeftCursor;
1562 }
1563 }
1564 else if(x >= fw - h) {
1565 if(y < h) {
1566 cursor = TopRightCursor;
1567 }
1568 else if(y >= fh - h) {
1569 cursor = BottomRightCursor;
1570 }
1571 else {
1572 cursor = RightCursor;
1573 }
1574 }
1575 else if(y < h) { /* also include title bar in top border area */
1576 cursor = TopCursor;
1577 }
1578 else if(y >= fh - h) {
1579 cursor = BottomCursor;
1580 }
1581 else {
1582 cursor = Scr->FrameCursor;
1583 }
1584 attr.cursor = cursor;
1586 tmp_win->curcurs = cursor;
1587}
1588
1589
1590
1591/*
1592 * End of code. Random doc/notes follow.
1593 */
1594
1595
1596/*
1597 * n.b.: Old doc about squeezed title. Not recently vetted. I'm pretty
1598 * sure it's definitely wrong for the 3D-borders case at the least.
1599 * Should be updated and migrated into developer docs at some point.
1600 *
1601 * Squeezed Title:
1602 *
1603 * tmp->title_x
1604 * 0 |
1605 * tmp->title_y ........+--------------+......... -+,- tmp->frame_bw
1606 * 0 : ......| +----------+ |....... : -++
1607 * : : | | | | : : ||-Scr->TitleHeight
1608 * : : | | | | : : ||
1609 * +-------+ +----------+ +--------+ -+|-tmp->title_height
1610 * | +---------------------------+ | --+
1611 * | | | |
1612 * | | | |
1613 * | | | |
1614 * | | | |
1615 * | | | |
1616 * | +---------------------------+ |
1617 * +-------------------------------+
1618 *
1619 *
1620 * Unsqueezed Title:
1621 *
1622 * tmp->title_x
1623 * | 0
1624 * tmp->title_y +-------------------------------+ -+,tmp->frame_bw
1625 * 0 | +---------------------------+ | -+'
1626 * | | | | |-Scr->TitleHeight
1627 * | | | | |
1628 * + +---------------------------+ + -+
1629 * |-+---------------------------+-|
1630 * | | | |
1631 * | | | |
1632 * | | | |
1633 * | | | |
1634 * | | | |
1635 * | +---------------------------+ |
1636 * +-------------------------------+
1637 *
1638 *
1639 *
1640 * Dimensions and Positions:
1641 *
1642 * frame orgin (0, 0)
1643 * frame upper left border (-tmp->frame_bw, -tmp->frame_bw)
1644 * frame size w/o border tmp->frame_width , tmp->frame_height
1645 * frame/title border width tmp->frame_bw
1646 * extra title height w/o bdr tmp->title_height = TitleHeight + frame_bw
1647 * title window height Scr->TitleHeight
1648 * title origin w/o border (tmp->title_x, tmp->title_y)
1649 * client origin (0, Scr->TitleHeight + tmp->frame_bw)
1650 * client size tmp->attr.width , tmp->attr.height
1651 *
1652 * When shaping, need to remember that the width and height of rectangles
1653 * are really deltax and deltay to lower right handle corner, so they need
1654 * to have -1 subtracted from would normally be the actual extents.
1655 */
static int PlaceX
Definition add_window.c:82
@ SIJ_CENTER
Definition ctwm.h:173
@ SIJ_LEFT
Definition ctwm.h:172
@ SIJ_RIGHT
Definition ctwm.h:174
char * ProgramName
Definition ctwm_main.c:146
Cursor RightCursor
Definition ctwm.h:354
TitleJust
Definition ctwm.h:202
@ TJ_CENTER
Definition ctwm.h:205
@ TJ_RIGHT
Definition ctwm.h:206
@ TJ_UNDEF
Definition ctwm.h:203
@ TJ_LEFT
Definition ctwm.h:204
Cursor BottomCursor
Definition ctwm.h:354
Display * dpy
Definition ctwm_main.c:84
Cursor LeftCursor
Definition ctwm.h:353
Cursor BottomRightCursor
Definition ctwm.h:354
Cursor TopRightCursor
Definition ctwm.h:354
#define FB(fix_fore, fix_back)
Definition ctwm.h:119
Cursor TopCursor
Definition ctwm_main.c:112
Cursor BottomLeftCursor
Definition ctwm.h:353
bool HasShape
Definition ctwm_main.c:90
Cursor TopLeftCursor
Definition ctwm.h:353
#define Scr
void Draw3DBorder(Window w, int x, int y, int width, int height, int bw, ColorPair cp, ButtonState state, bool fill, bool forcebw)
Definition drawing.c:34
ButtonState
Definition drawing.h:8
@ off
Definition drawing.h:8
@ on
Definition drawing.h:8
Image * GetImage(const char *name, ColorPair cp)
Definition image.c:37
Image * AllocImage(void)
Definition image.c:158
#define TBPM_QUESTION
Definition image.h:43
Pixmap mk_blackgray_pixmap(const char *which, Drawable dw, unsigned long fg, unsigned long bg)
void get_blackgray_size(int *width, int *height)
int y
Definition menus.c:70
int x
Definition menus.c:69
void ResizeOccupyWindow(TwmWindow *win)
Definition occupation.c:874
static int len
Definition parse.c:75
RArea RAreaNew(int x, int y, int width, int height)
Construct an RArea from given components.
Definition r_area.c:19
int RLayoutFindBottomEdge(const RLayout *self, const RArea *area)
Find the bottom of the top stripe of self that area fits into.
Definition r_layout.c:424
int RLayoutFindRightEdge(const RLayout *self, const RArea *area)
Find the right edge of the left-most stripe of self that area fits into.
Definition r_layout.c:503
int RLayoutFindLeftEdge(const RLayout *self, const RArea *area)
Find the left edge of the right-most stripe of self that area fits into.
Definition r_layout.c:489
int RLayoutFindTopEdge(const RLayout *self, const RArea *area)
Find the top of the bottom stripe of self that area fits into.
Definition r_layout.c:438
Pixel shadd
Definition ctwm.h:141
Pixel shadc
Definition ctwm.h:141
A particular extent of space.
Definition r_structs.h:16
int num
Definition ctwm.h:179
Window window
Definition ctwm.h:165
Info and control for every X Window we take over.
#define MKWIN()
void CreateWindowTitlebarButtons(TwmWindow *tmp_win)
void SetBorderCursor(TwmWindow *tmp_win, int x, int y)
CornerType
@ BottomRight
@ TopLeft
@ TopRight
@ BottomLeft
void PaintTitle(TwmWindow *tmp_win)
void ComputeTitleLocation(TwmWindow *tmp)
void SetFrameShape(TwmWindow *tmp)
void PaintTitleButton(TwmWindow *tmp_win, TBWindow *tbw)
void SetupWindow(TwmWindow *tmp_win, int x, int y, int w, int h, int bw)
static void Draw3DCorner(Window w, int x, int y, int width, int height, int thick, int bw, ColorPair cp, CornerType type)
void PaintTitleButtons(TwmWindow *tmp_win)
void PaintBorders(TwmWindow *tmp_win, bool focus)
#define CORNERLEN
#define MARGIN
void SetupFrame(TwmWindow *tmp_win, int x, int y, int w, int h, int bw, bool sendEvent)
static void CreateHighlightWindows(TwmWindow *tmp_win)
static void ComputeWindowTitleOffsets(TwmWindow *tmp_win, unsigned int width, bool squeeze)
static void CreateLowlightWindows(TwmWindow *tmp_win)
void DeleteHighlightWindows(TwmWindow *tmp_win)
bool visible(const TwmWindow *tmp_win)
Definition win_utils.c:341
void WMapSetupWindow(TwmWindow *win, int x, int y, int w, int h)