- s/sprintf/snprintf
[wmaker-crm.git] / src / balloon.c
blob69119560c8b3a5966482a3ca0652e521d9c7750b
1 /*
2 * Window Maker window manager
3 *
4 * Copyright (c) 1998 Alfredo K. Kojima
5 *
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,
19 * USA.
22 #include "wconfig.h"
24 #ifdef BALLOON_TEXT
26 #include <X11/Xlib.h>
27 #include <X11/Xutil.h>
28 #ifdef SHAPED_BALLOON
29 #include <X11/extensions/shape.h>
30 #endif
32 #include <stdlib.h>
33 #include <string.h>
35 #include <wraster.h>
37 #include "WindowMaker.h"
38 #include "screen.h"
39 #include "texture.h"
40 #include "wcore.h"
41 #include "framewin.h"
42 #include "icon.h"
43 #include "appicon.h"
44 #include "funcs.h"
45 #include "workspace.h"
46 #include "balloon.h"
50 extern WPreferences wPreferences;
52 typedef struct _WBalloon {
53 Window window;
55 #ifdef SHAPED_BALLOON
56 GC monoGC;
57 #endif
58 int prevType;
60 Window objectWindow;
61 char *text;
62 int h;
64 WMHandlerID timer;
66 Pixmap contents;
68 char mapped;
69 char ignoreTimer;
70 } WBalloon;
73 #define TOP 0
74 #define BOTTOM 1
75 #define LEFT 0
76 #define RIGHT 2
78 #define TLEFT (TOP|LEFT)
79 #define TRIGHT (TOP|RIGHT)
80 #define BLEFT (BOTTOM|LEFT)
81 #define BRIGHT (BOTTOM|RIGHT)
84 #ifdef SHAPED_BALLOON
86 #define SPACE 12
89 static void
90 drawBalloon(Pixmap pix, GC gc, int x, int y, int w, int h, int side)
92 int rad = h*3/10;
93 XPoint pt[3];
95 XFillArc(dpy, pix, gc, x, y, rad, rad, 90*64, 90*64);
96 XFillArc(dpy, pix, gc, x, y+h-1-rad, rad, rad, 180*64, 90*64);
98 XFillArc(dpy, pix, gc, x+w-1-rad, y, rad, rad, 0*64, 90*64);
99 XFillArc(dpy, pix, gc, x+w-1-rad, y+h-1-rad, rad, rad, 270*64, 90*64);
101 XFillRectangle(dpy, pix, gc, x, y+rad/2, w, h-rad);
102 XFillRectangle(dpy, pix, gc, x+rad/2, y, w-rad, h);
104 if (side & BOTTOM) {
105 pt[0].y = y+h-1;
106 pt[1].y = y+h-1+SPACE;
107 pt[2].y = y+h-1;
108 } else {
109 pt[0].y = y;
110 pt[1].y = y-SPACE;
111 pt[2].y = y;
113 if (side & RIGHT) {
114 pt[0].x = x+w-h+2*h/16;
115 pt[1].x = x+w-h+11*h/16;
116 pt[2].x = x+w-h+7*h/16;
117 } else {
118 pt[0].x = x+h-2*h/16;
119 pt[1].x = x+h-11*h/16;
120 pt[2].x = x+h-7*h/16;
122 XFillPolygon(dpy, pix, gc, pt, 3, Convex, CoordModeOrigin);
126 static Pixmap
127 makePixmap(WScreen *scr, int width, int height, int side, Pixmap *mask)
129 WBalloon *bal = scr->balloon;
130 Pixmap bitmap;
131 Pixmap pixmap;
132 int x, y;
134 bitmap = XCreatePixmap(dpy, scr->root_win, width+SPACE, height+SPACE, 1);
136 if (!bal->monoGC) {
137 bal->monoGC = XCreateGC(dpy, bitmap, 0, NULL);
139 XSetForeground(dpy, bal->monoGC, 0);
140 XFillRectangle(dpy, bitmap, bal->monoGC, 0, 0, width+SPACE, height+SPACE);
142 pixmap = XCreatePixmap(dpy, scr->root_win, width+SPACE, height+SPACE,
143 scr->w_depth);
144 XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
145 XFillRectangle(dpy, pixmap, scr->draw_gc, 0, 0, width+SPACE, height+SPACE);
147 if (side & BOTTOM) {
148 y = 0;
149 } else {
150 y = SPACE;
152 x = 0;
154 XSetForeground(dpy, bal->monoGC, 1);
155 drawBalloon(bitmap, bal->monoGC, x, y, width, height, side);
156 XSetForeground(dpy, scr->draw_gc, scr->white_pixel);
157 drawBalloon(pixmap, scr->draw_gc, x+1, y+1, width-2, height-2, side);
159 *mask = bitmap;
161 return pixmap;
165 static void
166 showText(WScreen *scr, int x, int y, int h, int w, char *text)
168 int width;
169 int height;
170 Pixmap pixmap;
171 Pixmap mask;
172 WMFont *font = scr->info_text_font;
173 int side = 0;
174 int ty;
175 int bx, by;
177 if (scr->balloon->contents)
178 XFreePixmap(dpy, scr->balloon->contents);
180 width = WMWidthOfString(font, text, strlen(text))+16;
181 height = WMFontHeight(font) + 4;
183 if (height < 16)
184 height = 16;
185 if (width < height)
186 width = height;
189 if (x + width > scr->scr_width) {
190 side = RIGHT;
191 bx = x - width + w/2;
192 if (bx < 0)
193 bx = 0;
194 } else {
195 side = LEFT;
196 bx = x + w/2;
198 if (bx + width > scr->scr_width)
199 bx = scr->scr_width - width;
201 if (y - (height + SPACE) < 0) {
202 side |= TOP;
203 by = y+h-1;
204 ty = SPACE;
205 } else {
206 side |= BOTTOM;
207 by = y - (height + SPACE);
208 ty = 0;
210 pixmap = makePixmap(scr, width, height, side, &mask);
212 XSetForeground(dpy, scr->info_text_gc, scr->black_pixel);
214 WMDrawString(scr->wmscreen, pixmap, scr->info_text_gc, font, 8,
215 ty + (height - WMFontHeight(font))/2,
216 text, strlen(text));
218 XSetWindowBackgroundPixmap(dpy, scr->balloon->window, pixmap);
219 scr->balloon->contents = pixmap;
221 XResizeWindow(dpy, scr->balloon->window, width, height+SPACE);
222 XShapeCombineMask(dpy, scr->balloon->window, ShapeBounding, 0, 0, mask,
223 ShapeSet);
224 XFreePixmap(dpy, mask);
225 XMoveWindow(dpy, scr->balloon->window, bx, by);
226 XMapRaised(dpy, scr->balloon->window);
228 scr->balloon->mapped = 1;
230 #else /* !SHAPED_BALLOON */
231 static void
232 showText(WScreen *scr, int x, int y, int h, int w, char *text)
234 int width;
235 int height;
236 Pixmap pixmap;
237 WMFont *font = scr->info_text_font;
239 if (scr->balloon->contents)
240 XFreePixmap(dpy, scr->balloon->contents);
242 width = WMWidthOfString(font, text, strlen(text))+8;
243 height = WMFontHeight(font) + 4;
245 if (x < 0)
246 x = 0;
247 else if (x + width > scr->scr_width-1)
248 x = scr->scr_width - width;
250 if (y - height - 2 < 0) {
251 y += h;
252 if (y < 0)
253 y = 0;
254 } else {
255 y -= height + 2;
258 if (scr->window_title_texture[0])
259 XSetForeground(dpy, scr->draw_gc,
260 scr->window_title_texture[0]->any.color.pixel);
261 else
262 XSetForeground(dpy, scr->draw_gc, scr->light_pixel);
264 pixmap = XCreatePixmap(dpy, scr->root_win, width, height, scr->w_depth);
265 XFillRectangle(dpy, pixmap, scr->draw_gc, 0, 0, width, height);
267 XSetForeground(dpy, scr->info_text_gc, scr->window_title_pixel[0]);
269 WMDrawString(scr->wmscreen, pixmap, scr->info_text_gc, font,
270 4, 2, text, strlen(text));
272 XResizeWindow(dpy, scr->balloon->window, width, height);
273 XMoveWindow(dpy, scr->balloon->window, x, y);
275 XSetWindowBackgroundPixmap(dpy, scr->balloon->window, pixmap);
276 XClearWindow(dpy, scr->balloon->window);
277 XMapRaised(dpy, scr->balloon->window);
279 scr->balloon->contents = pixmap;
281 scr->balloon->mapped = 1;
283 #endif /* !SHAPED_BALLOON */
286 static void
287 showBalloon(WScreen *scr)
289 int x, y;
290 Window foow;
291 unsigned foo, w;
293 if (scr->balloon) {
294 scr->balloon->timer = NULL;
295 scr->balloon->ignoreTimer = 1;
298 if (!XGetGeometry(dpy, scr->balloon->objectWindow, &foow, &x, &y,
299 &w, &foo, &foo, &foo)) {
300 scr->balloon->prevType = 0;
301 return;
303 showText(scr, x, y, scr->balloon->h, w, scr->balloon->text);
308 static void
309 frameBalloon(WObjDescriptor *object)
311 WFrameWindow *fwin = (WFrameWindow*)object->parent;
312 WScreen *scr = fwin->core->screen_ptr;
314 if (fwin->titlebar != object->self
315 || !fwin->flags.is_client_window_frame) {
316 wBalloonHide(scr);
317 return;
319 if (fwin->title && fwin->flags.incomplete_title) {
320 scr->balloon->h = (fwin->titlebar ? fwin->titlebar->height : 0);
321 scr->balloon->text = wstrdup(fwin->title);
322 scr->balloon->objectWindow = fwin->core->window;
323 scr->balloon->timer = WMAddTimerHandler(BALLOON_DELAY,
324 (WMCallback*)showBalloon, scr);
329 static void
330 miniwindowBalloon(WObjDescriptor *object)
332 WIcon *icon = (WIcon*)object->parent;
333 WScreen *scr = icon->core->screen_ptr;
335 if (!icon->icon_name) {
336 wBalloonHide(scr);
337 return;
339 scr->balloon->h = icon->core->height;
340 scr->balloon->text = wstrdup(icon->icon_name);
341 scr->balloon->objectWindow = icon->core->window;
342 if ((scr->balloon->prevType == object->parent_type
343 || scr->balloon->prevType == WCLASS_APPICON)
344 && scr->balloon->ignoreTimer) {
345 XUnmapWindow(dpy, scr->balloon->window);
346 showBalloon(scr);
347 } else {
348 scr->balloon->timer = WMAddTimerHandler(BALLOON_DELAY,
349 (WMCallback*)showBalloon, scr);
355 static void
356 appiconBalloon(WObjDescriptor *object)
358 WAppIcon *aicon = (WAppIcon*)object->parent;
359 WScreen *scr = aicon->icon->core->screen_ptr;
360 char *tmp;
362 if (aicon->command && aicon->wm_class) {
363 int len = strlen(aicon->command)+strlen(aicon->wm_class)+8;
364 tmp = wmalloc(len);
365 snprintf(tmp, len, "%s (%s)", aicon->wm_class, aicon->command);
366 scr->balloon->text = tmp;
367 } else if (aicon->command) {
368 scr->balloon->text = wstrdup(aicon->command);
369 } else if (aicon->wm_class) {
370 scr->balloon->text = wstrdup(aicon->wm_class);
371 } else {
372 wBalloonHide(scr);
373 return;
375 scr->balloon->h = aicon->icon->core->height-2;
377 scr->balloon->objectWindow = aicon->icon->core->window;
378 if ((scr->balloon->prevType == object->parent_type
379 || scr->balloon->prevType == WCLASS_MINIWINDOW)
380 && scr->balloon->ignoreTimer) {
381 XUnmapWindow(dpy, scr->balloon->window);
382 showBalloon(scr);
383 } else {
384 scr->balloon->timer = WMAddTimerHandler(BALLOON_DELAY,
385 (WMCallback*)showBalloon, scr);
391 void
392 wBalloonInitialize(WScreen *scr)
394 WBalloon *bal;
395 XSetWindowAttributes attribs;
396 unsigned long vmask;
398 bal = wmalloc(sizeof(WBalloon));
399 memset(bal, 0, sizeof(WBalloon));
401 scr->balloon = bal;
403 vmask = CWSaveUnder|CWOverrideRedirect|CWColormap|CWBackPixel
404 |CWBorderPixel;
405 attribs.save_under = True;
406 attribs.override_redirect = True;
407 attribs.colormap = scr->w_colormap;
408 attribs.background_pixel = scr->icon_back_texture->normal.pixel;
409 attribs.border_pixel = 0; /* do not care */
411 bal->window = XCreateWindow(dpy, scr->root_win, 1, 1, 10, 10, 1,
412 scr->w_depth, CopyFromParent,
413 scr->w_visual, vmask, &attribs);
414 #if 0
415 /* select EnterNotify to so that the balloon will be unmapped
416 * when the pointer is moved over it */
417 XSelectInput(dpy, bal->window, EnterWindowMask);
418 #endif
423 void
424 wBalloonEnteredObject(WScreen *scr, WObjDescriptor *object)
426 WBalloon *balloon = scr->balloon;
428 if (balloon->timer) {
429 WMDeleteTimerHandler(balloon->timer);
430 balloon->timer = NULL;
431 balloon->ignoreTimer = 0;
434 if (scr->balloon->text)
435 wfree(scr->balloon->text);
436 scr->balloon->text = NULL;
438 if (!object) {
439 wBalloonHide(scr);
440 balloon->ignoreTimer = 0;
441 return;
443 switch (object->parent_type) {
444 case WCLASS_FRAME:
445 if (wPreferences.window_balloon) {
446 frameBalloon(object);
448 break;
450 case WCLASS_DOCK_ICON:
451 if (object->parent != scr->clip_icon && wPreferences.appicon_balloon)
452 appiconBalloon(object);
453 else
454 wBalloonHide(scr);
455 break;
457 case WCLASS_MINIWINDOW:
458 if (wPreferences.miniwin_balloon) {
459 miniwindowBalloon(object);
461 break;
462 case WCLASS_APPICON:
463 if (wPreferences.appicon_balloon)
464 appiconBalloon(object);
465 break;
466 default:
467 wBalloonHide(scr);
468 break;
470 scr->balloon->prevType = object->parent_type;
475 void
476 wBalloonHide(WScreen *scr)
478 if (scr) {
479 if (scr->balloon->mapped) {
480 XUnmapWindow(dpy, scr->balloon->window);
481 scr->balloon->mapped = 0;
482 } else if (scr->balloon->timer) {
483 WMDeleteTimerHandler(scr->balloon->timer);
484 scr->balloon->timer = NULL;
486 scr->balloon->prevType = 0;
489 #endif