2 * Window Maker window manager
4 * Copyright (c) 1998-2003 Alfredo K. Kojima
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
27 #include <X11/Xutil.h>
29 #include <X11/extensions/shape.h>
37 #include "WindowMaker.h"
45 #include "workspace.h"
50 extern WPreferences wPreferences
;
52 typedef struct _WBalloon
{
78 #define TLEFT (TOP|LEFT)
79 #define TRIGHT (TOP|RIGHT)
80 #define BLEFT (BOTTOM|LEFT)
81 #define BRIGHT (BOTTOM|RIGHT)
85 countLines(const char *text
)
91 if (*p
== '\n' && p
[1] != 0)
100 getMaxStringWidth(WMFont
*font
, char *text
)
109 wt
= WMWidthOfString(font
, pb
, pos
);
120 wt
= WMWidthOfString(font
, pb
, pos
);
129 drawMultiLineString(WMScreen
*scr
, Pixmap pixmap
, WMColor
*color
,
130 WMFont
*font
, int x
, int y
, char *text
, int len
)
135 int height
= WMFontHeight(font
);
137 while (*p
&& p
- text
< len
) {
139 WMDrawString(scr
, pixmap
, color
, font
, x
, y
+ l
* height
, pb
,
150 WMDrawString(scr
, pixmap
, color
, font
, x
, y
+ l
* height
, pb
, pos
);
157 #ifdef SHAPED_BALLOON
163 drawBalloon(WScreen
*scr
, Pixmap bitmap
, Pixmap pix
, int x
, int y
, int w
,
166 GC bgc
= scr
->balloon
->monoGC
;
167 GC gc
= scr
->draw_gc
;
168 int rad
= h
* 3 / 10;
169 XPoint pt
[3], ipt
[3];
173 XSetForeground(dpy
, bgc
, 1);
175 XFillArc(dpy
, bitmap
, bgc
, x
, y
, rad
, rad
, 90 * 64, 90 * 64);
176 XFillArc(dpy
, bitmap
, bgc
, x
, y
+ h
- 1 - rad
, rad
, rad
, 180 * 64,
179 XFillArc(dpy
, bitmap
, bgc
, x
+ w
- 1 - rad
, y
, rad
, rad
, 0 * 64,
181 XFillArc(dpy
, bitmap
, bgc
, x
+ w
- 1 - rad
, y
+ h
- 1 - rad
, rad
, rad
,
184 XFillRectangle(dpy
, bitmap
, bgc
, x
, y
+ rad
/ 2, w
, h
- rad
);
185 XFillRectangle(dpy
, bitmap
, bgc
, x
+ rad
/ 2, y
, w
- rad
, h
);
188 XSetForeground(dpy
, gc
, scr
->white_pixel
);
190 XFillArc(dpy
, pix
, gc
, x
+ 1, y
+ 1, rad
, rad
, 90 * 64, 90 * 64);
191 XFillArc(dpy
, pix
, gc
, x
+ 1, y
+ h
- 2 - rad
, rad
, rad
, 180 * 64,
194 XFillArc(dpy
, pix
, gc
, x
+ w
- 2 - rad
, y
+ 1, rad
, rad
, 0 * 64,
196 XFillArc(dpy
, pix
, gc
, x
+ w
- 2 - rad
, y
+ h
- 2 - rad
, rad
, rad
,
199 XFillRectangle(dpy
, pix
, gc
, x
+ 1, y
+ 1 + rad
/ 2, w
- 2,
201 XFillRectangle(dpy
, pix
, gc
, x
+ 1 + rad
/ 2, y
+ 1, w
- 2 - rad
,
206 pt
[1].y
= y
+ h
- 1 + SPACE
;
208 ipt
[0].y
= pt
[0].y
- 1;
209 ipt
[1].y
= pt
[1].y
- 1;
210 ipt
[2].y
= pt
[2].y
- 1;
215 ipt
[0].y
= pt
[0].y
+ 1;
216 ipt
[1].y
= pt
[1].y
+ 1;
217 ipt
[2].y
= pt
[2].y
+ 1;
220 /*w1 = WMAX(h, 24); */
224 pt
[0].x
= x
+ w
- w1
+ 2 * w1
/ 16;
225 pt
[1].x
= x
+ w
- w1
+ 11 * w1
/ 16;
226 pt
[2].x
= x
+ w
- w1
+ 7 * w1
/ 16;
227 ipt
[0].x
= x
+ 1 + w
- w1
+ 2 * (w1
- 1) / 16;
228 ipt
[1].x
= x
+ 1 + w
- w1
+ 11 * (w1
- 1) / 16;
229 ipt
[2].x
= x
+ 1 + w
- w1
+ 7 * (w1
- 1) / 16;
230 /*ipt[0].x = pt[0].x+1;
232 ipt[2].x = pt[2].x; */
234 pt
[0].x
= x
+ w1
- 2 * w1
/ 16;
235 pt
[1].x
= x
+ w1
- 11 * w1
/ 16;
236 pt
[2].x
= x
+ w1
- 7 * w1
/ 16;
237 ipt
[0].x
= x
- 1 + w1
- 2 * (w1
- 1) / 16;
238 ipt
[1].x
= x
- 1 + w1
- 11 * (w1
- 1) / 16;
239 ipt
[2].x
= x
- 1 + w1
- 7 * (w1
- 1) / 16;
240 /*ipt[0].x = pt[0].x-1;
242 ipt[2].x = pt[2].x; */
245 XFillPolygon(dpy
, bitmap
, bgc
, pt
, 3, Convex
, CoordModeOrigin
);
246 XFillPolygon(dpy
, pix
, gc
, ipt
, 3, Convex
, CoordModeOrigin
);
249 XSetForeground(dpy
, gc
, scr
->black_pixel
);
251 XDrawLines(dpy
, pix
, gc
, pt
, 3, CoordModeOrigin
);
259 XDrawLines(dpy
, pix
, gc
, pt
, 3, CoordModeOrigin
);
264 makePixmap(WScreen
*scr
, int width
, int height
, int side
, Pixmap
*mask
)
266 WBalloon
*bal
= scr
->balloon
;
272 XCreatePixmap(dpy
, scr
->root_win
, width
+ SPACE
, height
+ SPACE
,
276 bal
->monoGC
= XCreateGC(dpy
, bitmap
, 0, NULL
);
278 XSetForeground(dpy
, bal
->monoGC
, 0);
279 XFillRectangle(dpy
, bitmap
, bal
->monoGC
, 0, 0, width
+ SPACE
,
283 XCreatePixmap(dpy
, scr
->root_win
, width
+ SPACE
, height
+ SPACE
,
285 XSetForeground(dpy
, scr
->draw_gc
, scr
->black_pixel
);
286 XFillRectangle(dpy
, pixmap
, scr
->draw_gc
, 0, 0, width
+ SPACE
,
296 drawBalloon(scr
, bitmap
, pixmap
, x
, y
, width
, height
, side
);
305 showText(WScreen
*scr
, int x
, int y
, int h
, int w
, char *text
)
311 WMFont
*font
= scr
->info_text_font
;
316 if (scr
->balloon
->contents
)
317 XFreePixmap(dpy
, scr
->balloon
->contents
);
319 width
= getMaxStringWidth(font
, text
) + 16;
320 height
= countLines(text
) * WMFontHeight(font
) + 4;
328 if (x
+ width
> scr
->scr_width
) {
330 bx
= x
- width
+ w
/ 2;
337 if (bx
+ width
> scr
->scr_width
)
338 bx
= scr
->scr_width
- width
;
340 if (y
- (height
+ SPACE
) < 0) {
346 by
= y
- (height
+ SPACE
);
349 pixmap
= makePixmap(scr
, width
, height
, side
, &mask
);
351 drawMultiLineString(scr
->wmscreen
, pixmap
, scr
->black
, font
, 8, ty
+ 2,
354 XSetWindowBackgroundPixmap(dpy
, scr
->balloon
->window
, pixmap
);
355 scr
->balloon
->contents
= pixmap
;
357 XResizeWindow(dpy
, scr
->balloon
->window
, width
, height
+ SPACE
);
358 XShapeCombineMask(dpy
, scr
->balloon
->window
, ShapeBounding
, 0, 0, mask
,
360 XFreePixmap(dpy
, mask
);
361 XMoveWindow(dpy
, scr
->balloon
->window
, bx
, by
);
362 XMapRaised(dpy
, scr
->balloon
->window
);
365 scr
->balloon
->mapped
= 1;
367 #else /* !SHAPED_BALLOON */
371 showText(WScreen
*scr
, int x
, int y
, int h
, int w
, char *text
)
376 WMFont
*font
= scr
->info_text_font
;
378 if (scr
->balloon
->contents
)
379 XFreePixmap(dpy
, scr
->balloon
->contents
);
381 width
= getMaxStringWidth(font
, text
) + 8;
382 /*width = WMWidthOfString(font, text, strlen(text))+8;*/
383 height
= countLines(text
) * WMFontHeight(font
) + 4;
387 else if (x
+ width
> scr
->scr_width
- 1)
388 x
= scr
->scr_width
- width
;
390 if (y
- height
- 2 < 0) {
398 if (scr
->window_title_texture
[0])
399 XSetForeground(dpy
, scr
->draw_gc
,
400 scr
->window_title_texture
[0]->any
.color
.pixel
);
402 XSetForeground(dpy
, scr
->draw_gc
, scr
->light_pixel
);
405 XCreatePixmap(dpy
, scr
->root_win
, width
, height
, scr
->w_depth
);
406 XFillRectangle(dpy
, pixmap
, scr
->draw_gc
, 0, 0, width
, height
);
408 drawMultiLineString(scr
->wmscreen
, pixmap
, scr
->window_title_color
[0],
409 font
, 4, 2, text
, strlen(text
));
411 XResizeWindow(dpy
, scr
->balloon
->window
, width
, height
);
412 XMoveWindow(dpy
, scr
->balloon
->window
, x
, y
);
414 XSetWindowBackgroundPixmap(dpy
, scr
->balloon
->window
, pixmap
);
415 XClearWindow(dpy
, scr
->balloon
->window
);
416 XMapRaised(dpy
, scr
->balloon
->window
);
418 scr
->balloon
->contents
= pixmap
;
420 scr
->balloon
->mapped
= 1;
422 #endif /* !SHAPED_BALLOON */
426 showBalloon(WScreen
*scr
)
433 scr
->balloon
->timer
= NULL
;
434 scr
->balloon
->ignoreTimer
= 1;
437 if (!XGetGeometry(dpy
, scr
->balloon
->objectWindow
, &foow
, &x
, &y
,
438 &w
, &foo
, &foo
, &foo
)) {
439 scr
->balloon
->prevType
= 0;
442 showText(scr
, x
, y
, scr
->balloon
->h
, w
, scr
->balloon
->text
);
447 frameBalloon(WObjDescriptor
*object
)
449 WFrameWindow
*fwin
= (WFrameWindow
*) object
->parent
;
450 WScreen
*scr
= fwin
->core
->screen_ptr
;
452 if (fwin
->titlebar
!= object
->self
453 || !fwin
->flags
.is_client_window_frame
) {
457 if (fwin
->title
&& fwin
->flags
.incomplete_title
) {
458 scr
->balloon
->h
= (fwin
->titlebar
? fwin
->titlebar
->height
: 0);
459 scr
->balloon
->text
= wstrdup(fwin
->title
);
460 scr
->balloon
->objectWindow
= fwin
->core
->window
;
461 scr
->balloon
->timer
= WMAddTimerHandler(BALLOON_DELAY
,
462 (WMCallback
*) showBalloon
,
469 miniwindowBalloon(WObjDescriptor
*object
)
471 WIcon
*icon
= (WIcon
*) object
->parent
;
472 WScreen
*scr
= icon
->core
->screen_ptr
;
474 if (!icon
->icon_name
) {
478 scr
->balloon
->h
= icon
->core
->height
;
479 scr
->balloon
->text
= wstrdup(icon
->icon_name
);
480 scr
->balloon
->objectWindow
= icon
->core
->window
;
481 if ((scr
->balloon
->prevType
== object
->parent_type
482 || scr
->balloon
->prevType
== WCLASS_APPICON
)
483 && scr
->balloon
->ignoreTimer
) {
484 XUnmapWindow(dpy
, scr
->balloon
->window
);
487 scr
->balloon
->timer
= WMAddTimerHandler(BALLOON_DELAY
,
488 (WMCallback
*) showBalloon
,
495 appiconBalloon(WObjDescriptor
*object
)
497 WAppIcon
*aicon
= (WAppIcon
*) object
->parent
;
498 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
501 if (aicon
->command
&& aicon
->wm_class
) {
502 int len
= strlen(aicon
->command
) + strlen(aicon
->wm_class
) + 8;
504 snprintf(tmp
, len
, "%s\n(%s)", aicon
->wm_class
, aicon
->command
);
505 scr
->balloon
->text
= tmp
;
506 } else if (aicon
->command
) {
507 scr
->balloon
->text
= wstrdup(aicon
->command
);
508 } else if (aicon
->wm_class
) {
509 scr
->balloon
->text
= wstrdup(aicon
->wm_class
);
514 scr
->balloon
->h
= aicon
->icon
->core
->height
- 2;
516 scr
->balloon
->objectWindow
= aicon
->icon
->core
->window
;
517 if ((scr
->balloon
->prevType
== object
->parent_type
518 || scr
->balloon
->prevType
== WCLASS_MINIWINDOW
)
519 && scr
->balloon
->ignoreTimer
) {
520 XUnmapWindow(dpy
, scr
->balloon
->window
);
523 scr
->balloon
->timer
= WMAddTimerHandler(BALLOON_DELAY
,
524 (WMCallback
*) showBalloon
,
532 wBalloonInitialize(WScreen
*scr
)
535 XSetWindowAttributes attribs
;
538 bal
= wmalloc(sizeof(WBalloon
));
539 memset(bal
, 0, sizeof(WBalloon
));
543 vmask
= CWSaveUnder
| CWOverrideRedirect
| CWColormap
| CWBackPixel
545 attribs
.save_under
= True
;
546 attribs
.override_redirect
= True
;
547 attribs
.colormap
= scr
->w_colormap
;
548 attribs
.background_pixel
= scr
->icon_back_texture
->normal
.pixel
;
549 attribs
.border_pixel
= 0; /* do not care */
551 bal
->window
= XCreateWindow(dpy
, scr
->root_win
, 1, 1, 10, 10, 1,
552 scr
->w_depth
, CopyFromParent
,
553 scr
->w_visual
, vmask
, &attribs
);
555 /* select EnterNotify to so that the balloon will be unmapped
556 * when the pointer is moved over it */
557 XSelectInput(dpy
, bal
->window
, EnterWindowMask
);
563 wBalloonEnteredObject(WScreen
*scr
, WObjDescriptor
*object
)
565 WBalloon
*balloon
= scr
->balloon
;
567 if (balloon
->timer
) {
568 WMDeleteTimerHandler(balloon
->timer
);
569 balloon
->timer
= NULL
;
570 balloon
->ignoreTimer
= 0;
573 if (scr
->balloon
->text
)
574 wfree(scr
->balloon
->text
);
575 scr
->balloon
->text
= NULL
;
579 balloon
->ignoreTimer
= 0;
582 switch (object
->parent_type
) {
584 if (wPreferences
.window_balloon
) {
585 frameBalloon(object
);
589 case WCLASS_DOCK_ICON
:
590 if (object
->parent
!= scr
->clip_icon
591 && wPreferences
.appicon_balloon
)
592 appiconBalloon(object
);
597 case WCLASS_MINIWINDOW
:
598 if (wPreferences
.miniwin_balloon
) {
599 miniwindowBalloon(object
);
603 if (wPreferences
.appicon_balloon
)
604 appiconBalloon(object
);
610 scr
->balloon
->prevType
= object
->parent_type
;
616 wBalloonHide(WScreen
*scr
)
619 if (scr
->balloon
->mapped
) {
620 XUnmapWindow(dpy
, scr
->balloon
->window
);
621 scr
->balloon
->mapped
= 0;
622 } else if (scr
->balloon
->timer
) {
623 WMDeleteTimerHandler(scr
->balloon
->timer
);
624 scr
->balloon
->timer
= NULL
;
626 scr
->balloon
->prevType
= 0;