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 along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include <X11/Xutil.h>
29 #include <X11/extensions/shape.h>
37 #include "WindowMaker.h"
43 #include "workspace.h"
47 typedef struct _WBalloon
{
72 #define TLEFT (TOP|LEFT)
73 #define TRIGHT (TOP|RIGHT)
74 #define BLEFT (BOTTOM|LEFT)
75 #define BRIGHT (BOTTOM|RIGHT)
77 static int countLines(const char *text
)
83 if (*p
== '\n' && p
[1] != 0)
90 static int getMaxStringWidth(WMFont
* font
, const char *text
)
99 wt
= WMWidthOfString(font
, pb
, pos
);
110 wt
= WMWidthOfString(font
, pb
, pos
);
118 drawMultiLineString(WMScreen
* scr
, Pixmap pixmap
, WMColor
* color
,
119 WMFont
*font
, int x
, int y
, const char *text
, int len
)
121 const char *p
= text
;
124 int height
= WMFontHeight(font
);
126 while (*p
&& p
- text
< len
) {
128 WMDrawString(scr
, pixmap
, color
, font
, x
, y
+ l
* height
, pb
, pos
);
138 WMDrawString(scr
, pixmap
, color
, font
, x
, y
+ l
* height
, pb
, pos
);
142 #ifdef SHAPED_BALLOON
146 static void drawBalloon(WScreen
* scr
, Pixmap bitmap
, Pixmap pix
, int x
, int y
, int w
, int h
, int side
)
148 GC bgc
= scr
->balloon
->monoGC
;
149 GC gc
= scr
->draw_gc
;
150 int rad
= h
* 3 / 10;
151 XPoint pt
[3], ipt
[3];
155 XSetForeground(dpy
, bgc
, 1);
157 XFillArc(dpy
, bitmap
, bgc
, x
, y
, rad
, rad
, 90 * 64, 90 * 64);
158 XFillArc(dpy
, bitmap
, bgc
, x
, y
+ h
- 1 - rad
, rad
, rad
, 180 * 64, 90 * 64);
160 XFillArc(dpy
, bitmap
, bgc
, x
+ w
- 1 - rad
, y
, rad
, rad
, 0 * 64, 90 * 64);
161 XFillArc(dpy
, bitmap
, bgc
, x
+ w
- 1 - rad
, y
+ h
- 1 - rad
, rad
, rad
, 270 * 64, 90 * 64);
163 XFillRectangle(dpy
, bitmap
, bgc
, x
, y
+ rad
/ 2, w
, h
- rad
);
164 XFillRectangle(dpy
, bitmap
, bgc
, x
+ rad
/ 2, y
, w
- rad
, h
);
167 XSetForeground(dpy
, gc
, scr
->white_pixel
);
169 XFillArc(dpy
, pix
, gc
, x
+ 1, y
+ 1, rad
, rad
, 90 * 64, 90 * 64);
170 XFillArc(dpy
, pix
, gc
, x
+ 1, y
+ h
- 2 - rad
, rad
, rad
, 180 * 64, 90 * 64);
172 XFillArc(dpy
, pix
, gc
, x
+ w
- 2 - rad
, y
+ 1, rad
, rad
, 0 * 64, 90 * 64);
173 XFillArc(dpy
, pix
, gc
, x
+ w
- 2 - rad
, y
+ h
- 2 - rad
, rad
, rad
, 270 * 64, 90 * 64);
175 XFillRectangle(dpy
, pix
, gc
, x
+ 1, y
+ 1 + rad
/ 2, w
- 2, h
- 2 - rad
);
176 XFillRectangle(dpy
, pix
, gc
, x
+ 1 + rad
/ 2, y
+ 1, w
- 2 - rad
, h
- 2);
180 pt
[1].y
= y
+ h
- 1 + SPACE
;
182 ipt
[0].y
= pt
[0].y
- 1;
183 ipt
[1].y
= pt
[1].y
- 1;
184 ipt
[2].y
= pt
[2].y
- 1;
189 ipt
[0].y
= pt
[0].y
+ 1;
190 ipt
[1].y
= pt
[1].y
+ 1;
191 ipt
[2].y
= pt
[2].y
+ 1;
194 /*w1 = WMAX(h, 24); */
198 pt
[0].x
= x
+ w
- w1
+ 2 * w1
/ 16;
199 pt
[1].x
= x
+ w
- w1
+ 11 * w1
/ 16;
200 pt
[2].x
= x
+ w
- w1
+ 7 * w1
/ 16;
201 ipt
[0].x
= x
+ 1 + w
- w1
+ 2 * (w1
- 1) / 16;
202 ipt
[1].x
= x
+ 1 + w
- w1
+ 11 * (w1
- 1) / 16;
203 ipt
[2].x
= x
+ 1 + w
- w1
+ 7 * (w1
- 1) / 16;
204 /*ipt[0].x = pt[0].x+1;
206 ipt[2].x = pt[2].x; */
208 pt
[0].x
= x
+ w1
- 2 * w1
/ 16;
209 pt
[1].x
= x
+ w1
- 11 * w1
/ 16;
210 pt
[2].x
= x
+ w1
- 7 * w1
/ 16;
211 ipt
[0].x
= x
- 1 + w1
- 2 * (w1
- 1) / 16;
212 ipt
[1].x
= x
- 1 + w1
- 11 * (w1
- 1) / 16;
213 ipt
[2].x
= x
- 1 + w1
- 7 * (w1
- 1) / 16;
214 /*ipt[0].x = pt[0].x-1;
216 ipt[2].x = pt[2].x; */
219 XFillPolygon(dpy
, bitmap
, bgc
, pt
, 3, Convex
, CoordModeOrigin
);
220 XFillPolygon(dpy
, pix
, gc
, ipt
, 3, Convex
, CoordModeOrigin
);
223 XSetForeground(dpy
, gc
, scr
->black_pixel
);
225 XDrawLines(dpy
, pix
, gc
, pt
, 3, CoordModeOrigin
);
233 XDrawLines(dpy
, pix
, gc
, pt
, 3, CoordModeOrigin
);
236 static Pixmap
makePixmap(WScreen
* scr
, int width
, int height
, int side
, Pixmap
* mask
)
238 WBalloon
*bal
= scr
->balloon
;
243 bitmap
= XCreatePixmap(dpy
, scr
->root_win
, width
+ SPACE
, height
+ SPACE
, 1);
246 bal
->monoGC
= XCreateGC(dpy
, bitmap
, 0, NULL
);
248 XSetForeground(dpy
, bal
->monoGC
, 0);
249 XFillRectangle(dpy
, bitmap
, bal
->monoGC
, 0, 0, width
+ SPACE
, height
+ SPACE
);
251 pixmap
= XCreatePixmap(dpy
, scr
->root_win
, width
+ SPACE
, height
+ SPACE
, scr
->w_depth
);
252 XSetForeground(dpy
, scr
->draw_gc
, scr
->black_pixel
);
253 XFillRectangle(dpy
, pixmap
, scr
->draw_gc
, 0, 0, width
+ SPACE
, height
+ SPACE
);
262 drawBalloon(scr
, bitmap
, pixmap
, x
, y
, width
, height
, side
);
269 static void showText(WScreen
*scr
, int x
, int y
, int h
, int w
, const char *text
)
275 WMFont
*font
= scr
->info_text_font
;
280 if (scr
->balloon
->contents
)
281 XFreePixmap(dpy
, scr
->balloon
->contents
);
283 width
= getMaxStringWidth(font
, text
) + 16;
284 height
= countLines(text
) * WMFontHeight(font
) + 4;
291 if (x
+ width
> scr
->scr_width
) {
293 bx
= x
- width
+ w
/ 2;
300 if (bx
+ width
> scr
->scr_width
)
301 bx
= scr
->scr_width
- width
;
303 if (y
- (height
+ SPACE
) < 0) {
309 by
= y
- (height
+ SPACE
);
312 pixmap
= makePixmap(scr
, width
, height
, side
, &mask
);
314 drawMultiLineString(scr
->wmscreen
, pixmap
, scr
->black
, font
, 8, ty
+ 2, text
, strlen(text
));
316 XSetWindowBackgroundPixmap(dpy
, scr
->balloon
->window
, pixmap
);
317 scr
->balloon
->contents
= pixmap
;
319 XResizeWindow(dpy
, scr
->balloon
->window
, width
, height
+ SPACE
);
320 XShapeCombineMask(dpy
, scr
->balloon
->window
, ShapeBounding
, 0, 0, mask
, ShapeSet
);
321 XFreePixmap(dpy
, mask
);
322 XMoveWindow(dpy
, scr
->balloon
->window
, bx
, by
);
323 XMapRaised(dpy
, scr
->balloon
->window
);
325 scr
->balloon
->mapped
= 1;
327 #else /* !SHAPED_BALLOON */
329 static void showText(WScreen
*scr
, int x
, int y
, int h
, int w
, const char *text
)
334 WMFont
*font
= scr
->info_text_font
;
336 if (scr
->balloon
->contents
)
337 XFreePixmap(dpy
, scr
->balloon
->contents
);
339 width
= getMaxStringWidth(font
, text
) + 8;
340 /*width = WMWidthOfString(font, text, strlen(text))+8; */
341 height
= countLines(text
) * WMFontHeight(font
) + 4;
345 else if (x
+ width
> scr
->scr_width
- 1)
346 x
= scr
->scr_width
- width
;
348 if (y
- height
- 2 < 0) {
356 if (scr
->window_title_texture
[0])
357 XSetForeground(dpy
, scr
->draw_gc
, scr
->window_title_texture
[0]->any
.color
.pixel
);
359 XSetForeground(dpy
, scr
->draw_gc
, scr
->light_pixel
);
361 pixmap
= XCreatePixmap(dpy
, scr
->root_win
, width
, height
, scr
->w_depth
);
362 XFillRectangle(dpy
, pixmap
, scr
->draw_gc
, 0, 0, width
, height
);
364 drawMultiLineString(scr
->wmscreen
, pixmap
, scr
->window_title_color
[0], font
, 4, 2, text
, strlen(text
));
366 XResizeWindow(dpy
, scr
->balloon
->window
, width
, height
);
367 XMoveWindow(dpy
, scr
->balloon
->window
, x
, y
);
369 XSetWindowBackgroundPixmap(dpy
, scr
->balloon
->window
, pixmap
);
370 XClearWindow(dpy
, scr
->balloon
->window
);
371 XMapRaised(dpy
, scr
->balloon
->window
);
373 scr
->balloon
->contents
= pixmap
;
375 scr
->balloon
->mapped
= 1;
377 #endif /* !SHAPED_BALLOON */
379 static void showBalloon(WScreen
* scr
)
386 scr
->balloon
->timer
= NULL
;
387 scr
->balloon
->ignoreTimer
= 1;
390 if (!XGetGeometry(dpy
, scr
->balloon
->objectWindow
, &foow
, &x
, &y
, &w
, &foo
, &foo
, &foo
)) {
391 scr
->balloon
->prevType
= 0;
394 showText(scr
, x
, y
, scr
->balloon
->h
, w
, scr
->balloon
->text
);
397 static void frameBalloon(WObjDescriptor
* object
)
399 WFrameWindow
*fwin
= (WFrameWindow
*) object
->parent
;
400 WScreen
*scr
= fwin
->core
->screen_ptr
;
402 if (fwin
->titlebar
!= object
->self
|| !fwin
->flags
.is_client_window_frame
) {
406 if (fwin
->title
&& fwin
->flags
.incomplete_title
) {
407 scr
->balloon
->h
= (fwin
->titlebar
? fwin
->titlebar
->height
: 0);
408 scr
->balloon
->text
= wstrdup(fwin
->title
);
409 scr
->balloon
->objectWindow
= fwin
->core
->window
;
410 scr
->balloon
->timer
= WMAddTimerHandler(BALLOON_DELAY
, (WMCallback
*) showBalloon
, scr
);
414 static void miniwindowBalloon(WObjDescriptor
* object
)
416 WIcon
*icon
= (WIcon
*) object
->parent
;
417 WScreen
*scr
= icon
->core
->screen_ptr
;
419 if (!icon
->icon_name
) {
423 scr
->balloon
->h
= icon
->core
->height
;
424 scr
->balloon
->text
= wstrdup(icon
->icon_name
);
425 scr
->balloon
->objectWindow
= icon
->core
->window
;
426 if ((scr
->balloon
->prevType
== object
->parent_type
|| scr
->balloon
->prevType
== WCLASS_APPICON
)
427 && scr
->balloon
->ignoreTimer
) {
428 XUnmapWindow(dpy
, scr
->balloon
->window
);
431 scr
->balloon
->timer
= WMAddTimerHandler(BALLOON_DELAY
, (WMCallback
*) showBalloon
, scr
);
435 static void appiconBalloon(WObjDescriptor
*object
)
437 WAppIcon
*aicon
= (WAppIcon
*) object
->parent
;
438 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
441 /* Show balloon if it is the Clip and the workspace name is > 5 chars */
442 if (object
->parent
== scr
->clip_icon
) {
443 if (strlen(scr
->workspaces
[scr
->current_workspace
]->name
) > 5) {
444 scr
->balloon
->text
= wstrdup(scr
->workspaces
[scr
->current_workspace
]->name
);
449 } else if (aicon
->command
&& aicon
->wm_class
) {
451 /* Check to see if it is a GNUstep app */
452 if (strcmp(aicon
->wm_class
, "GNUstep") == 0)
453 len
= strlen(aicon
->command
) + strlen(aicon
->wm_instance
) + 8;
455 len
= strlen(aicon
->command
) + strlen(aicon
->wm_class
) + 8;
457 /* Check to see if it is a GNUstep App */
458 if (strcmp(aicon
->wm_class
, "GNUstep") == 0)
459 snprintf(tmp
, len
, "%s\n(%s)", aicon
->wm_instance
, aicon
->command
);
461 snprintf(tmp
, len
, "%s\n(%s)", aicon
->wm_class
, aicon
->command
);
462 scr
->balloon
->text
= tmp
;
463 } else if (aicon
->command
) {
464 scr
->balloon
->text
= wstrdup(aicon
->command
);
465 } else if (aicon
->wm_class
) {
466 /* Check to see if it is a GNUstep App */
467 if (strcmp(aicon
->wm_class
, "GNUstep") == 0)
468 scr
->balloon
->text
= wstrdup(aicon
->wm_instance
);
470 scr
->balloon
->text
= wstrdup(aicon
->wm_class
);
475 scr
->balloon
->h
= aicon
->icon
->core
->height
- 2;
477 scr
->balloon
->objectWindow
= aicon
->icon
->core
->window
;
478 if ((scr
->balloon
->prevType
== object
->parent_type
|| scr
->balloon
->prevType
== WCLASS_MINIWINDOW
)
479 && scr
->balloon
->ignoreTimer
) {
480 XUnmapWindow(dpy
, scr
->balloon
->window
);
483 scr
->balloon
->timer
= WMAddTimerHandler(BALLOON_DELAY
, (WMCallback
*) showBalloon
, scr
);
487 void wBalloonInitialize(WScreen
* scr
)
490 XSetWindowAttributes attribs
;
493 bal
= wmalloc(sizeof(WBalloon
));
497 vmask
= CWSaveUnder
| CWOverrideRedirect
| CWColormap
| CWBackPixel
| CWBorderPixel
;
498 attribs
.save_under
= True
;
499 attribs
.override_redirect
= True
;
500 attribs
.colormap
= scr
->w_colormap
;
501 attribs
.background_pixel
= scr
->icon_back_texture
->normal
.pixel
;
502 attribs
.border_pixel
= 0; /* do not care */
504 bal
->window
= XCreateWindow(dpy
, scr
->root_win
, 1, 1, 10, 10, 1,
505 scr
->w_depth
, CopyFromParent
, scr
->w_visual
, vmask
, &attribs
);
507 /* select EnterNotify to so that the balloon will be unmapped
508 * when the pointer is moved over it */
509 XSelectInput(dpy
, bal
->window
, EnterWindowMask
);
513 void wBalloonEnteredObject(WScreen
* scr
, WObjDescriptor
* object
)
515 WBalloon
*balloon
= scr
->balloon
;
517 if (balloon
->timer
) {
518 WMDeleteTimerHandler(balloon
->timer
);
519 balloon
->timer
= NULL
;
520 balloon
->ignoreTimer
= 0;
523 if (scr
->balloon
->text
)
524 wfree(scr
->balloon
->text
);
525 scr
->balloon
->text
= NULL
;
529 balloon
->ignoreTimer
= 0;
533 switch (object
->parent_type
) {
535 if (wPreferences
.window_balloon
)
536 frameBalloon(object
);
538 case WCLASS_DOCK_ICON
:
539 if (wPreferences
.appicon_balloon
)
540 appiconBalloon(object
);
542 case WCLASS_MINIWINDOW
:
543 if (wPreferences
.miniwin_balloon
)
544 miniwindowBalloon(object
);
547 if (wPreferences
.appicon_balloon
)
548 appiconBalloon(object
);
554 scr
->balloon
->prevType
= object
->parent_type
;
557 void wBalloonHide(WScreen
* scr
)
560 if (scr
->balloon
->mapped
) {
561 XUnmapWindow(dpy
, scr
->balloon
->window
);
562 scr
->balloon
->mapped
= 0;
563 } else if (scr
->balloon
->timer
) {
564 WMDeleteTimerHandler(scr
->balloon
->timer
);
565 scr
->balloon
->timer
= NULL
;
567 scr
->balloon
->prevType
= 0;