1 /* wmDrawer (c) 2002-2004 Valery Febvre <vfebvre@easter-eggs.com>
2 *
3 * wmDrawer is a dock application (dockapp) which provides a
4 * drawer (button bar) to launch applications from.
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.
10 *
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.
15 *
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, USA.
19 */
20
21 #include <unistd.h>
22 #include <sys/types.h>
23 #include <sys/select.h>
24 #include <sys/wait.h>
25 #include <time.h>
26 #include <signal.h>
27
28 #if defined SOLARIS
29 #include <X11/Xlib.h>
30 #include <X11/Xutil.h>
31 #endif
32
33 #include "types_defs.h"
34
35 #include <X11/extensions/shape.h>
36 #include <X11/cursorfont.h>
37
38 #include "config.h"
39 #include "graphics.h"
40 #include "images.h"
41
42 #include "pixmaps/defaultDockIcon.xpm"
43 #include "pixmaps/defaultIconsBg.xpm"
44 #include "pixmaps/defaultIcon.xpm"
45 #include "pixmaps/defaultHighlightImg.xpm"
46
47 static void signalHandler (int signum);
48 static void quit (int status);
49 static void xfreeAllMemory (void);
50 static void buildDock (int argc, char *argv[]);
51 static void setDockIcon (void);
52 static void getDockPosition (void);
53 static void getDrawerPosition (int *x, int *y);
54 static void showDrawer (void);
55 static void hideDrawer (void);
56 static void buildDrawer (void);
57 static void buildTooltip (void);
58 static void highlightBtn (int col, int row);
59 static void highlightBtnShading (int col, int row);
60 static void highlightBtnTarget (int col, int row);
61 static void unhighlightBtn (Window btnWin);
62 static void rebuildApp (void);
63 static void execCmd (const char *cmd);
64 static void mySleep (unsigned long msec);
65 static int xEnterEventOrTimeout (XEvent *e, unsigned long msec);
66 static void convertMCoordIntoBCoord (int mcol, int mrow, int *bcol, int *brow);
67 static void eventLoop (void);
68
69 Display *display;
70 static Window dockWin, dockIconWin;
71 static drawingArea drawer, subDrawer, tooltip;
72 static XFontStruct *font;
73 drawerButton **btns;
74 static Cursor cursor;
75 static Pixmap highlightImgPix, highlightImgMask;
76
77 static unsigned int drawerOpened = 0;
78 static unsigned int dpyWidth, dpyHeight;
79 static unsigned int dockX, dockY;
80 static unsigned int drawerW, drawerH;
81 static unsigned int btnDim = 0;
82 static int highlightCol, highlightRow;
83
84 extern drawerConfig config;
85 extern unsigned int nbRows, nbCols, drawerOK;
86 extern unsigned int useDefaultIconsBg, useDefaultDockIcon, useDefaultHighlightImg;
87
main(int argc,char ** argv)88 int main (int argc, char **argv) {
89 struct sigaction action;
90
91 parseOptions (argc, argv);
92 parseConfig ();
93 buildDock (argc, argv);
94 setDockIcon ();
95 buildDrawer ();
96 buildTooltip ();
97 action.sa_handler = signalHandler;
98 sigemptyset (&action.sa_mask);
99 action.sa_flags = SA_NOCLDSTOP | SA_NODEFER;
100 sigaction (SIGTERM, &action, NULL);
101 sigaction (SIGINT, &action, NULL);
102 sigaction (SIGCHLD, &action, NULL);
103
104 eventLoop ();
105 return 0;
106 }
107
signalHandler(int signum)108 static void signalHandler (int signum) {
109 switch (signum) {
110 case SIGINT:
111 case SIGTERM:
112 quit (EXIT_SUCCESS);
113 break;
114 case SIGCHLD:
115 while (waitpid (-1, NULL, WNOHANG | WUNTRACED) > 0);
116 break;
117 default:
118 quit (EXIT_FAILURE);
119 }
120 }
121
quit(int status)122 static void quit (int status) {
123 printf ("Bye bye, thx to use %s\n", PACKAGE);
124 xfreeAllMemory ();
125 freeAllMemory ();
126 XCloseDisplay (display);
127 exit (status);
128 }
129
xfreeAllMemory(void)130 void xfreeAllMemory (void) {
131 unsigned int col, row;
132
133 /* free buttons' pixmap and GC */
134 for (col=0; col<nbCols; col++) {
135 for (row=0; row<nbRows; row++) {
136 XFreeGC (display, btns[col][row].da.gc);
137 if (btns[col][row].da.pixmap != None) {
138 XFreePixmap (display, btns[col][row].da.pixmap);
139 }
140 }
141 }
142 /* free tooltip GC and font */
143 XFreeGC (display, tooltip.gc);
144 XFreeFont (display, font);
145 /* destroy all windows and sub-windows */
146 if (drawerOK == 1) {
147 XDestroyWindow (display, drawer.win);
148 XDestroyWindow (display, tooltip.win);
149 }
150 }
151
buildDock(int argc,char * argv[])152 static void buildDock (int argc, char *argv[]) {
153 XClassHint *classhint = NULL;
154 XWMHints *wmhints = NULL;
155 XSizeHints *sizehints = NULL;
156 int tmp;
157
158 display = XOpenDisplay ("");
159 if (!display) {
160 printf ("%s error: couldn't open display\n", PACKAGE);
161 exit (EXIT_FAILURE);
162 }
163
164 /* query SHAPE extension */
165 if (!XShapeQueryExtension (display, &tmp, &tmp) && config.transparency > 0) {
166 printf ("%s warning: SHAPE extension not supported\n", PACKAGE);
167 config.transparency = 0;
168 }
169
170 // create dock wins
171 dockWin = XCreateSimpleWindow (display, DefaultRootWindow (display),
172 0, 0, config.dockW, config.dockH, 0, 0, 0);
173 dockIconWin = XCreateSimpleWindow (display, DefaultRootWindow (display),
174 0, 0, config.dockW, config.dockH,
175 0, 0, 0);
176
177 /* set ClassHint */
178 classhint = XAllocClassHint ();
179 if (!classhint) {
180 printf ("%s error: can't allocate memory for class hints!\n", PACKAGE);
181 exit (EXIT_FAILURE);
182 }
183 classhint->res_class = PACKAGE;
184 /* Ed Goforth <e.goforth@computer.org> */
185 /* Get the instanceName from the config file or command-line */
186 /* to set the client instance name (application name) */
187 /* so the window manager can differentiate between multiple instances. */
188 classhint->res_name = config.instanceName;
189 XSetClassHint (display, dockWin, classhint);
190 XSetClassHint (display, dockIconWin, classhint);
191 XFree (classhint);
192
193 // set name and icon name for dock wins
194 XStoreName (display, dockWin, PACKAGE);
195 XStoreName (display, dockIconWin, PACKAGE);
196 XSetIconName (display, dockWin, PACKAGE);
197 XSetIconName (display, dockIconWin, PACKAGE);
198
199 /* set WMHints */
200 wmhints = XAllocWMHints ();
201 if (!wmhints) {
202 printf ("%s error: can't allocate memory for wm hints!\n", PACKAGE);
203 exit (EXIT_FAILURE);
204 }
205 wmhints->flags = IconWindowHint | WindowGroupHint;
206 if (config.windowedMode == 0) {
207 wmhints->flags |= StateHint;
208 wmhints->initial_state = WithdrawnState;
209 }
210 wmhints->window_group = dockWin;
211 wmhints->icon_window = dockIconWin;
212 XSetWMHints (display, dockWin, wmhints);
213 XFree (wmhints);
214
215 /* set SizeHints */
216 sizehints = XAllocSizeHints ();
217 sizehints->flags = USSize;
218 if (config.windowedMode == 0) {
219 sizehints->flags |= USPosition;
220 sizehints->x = sizehints->y = 0;
221 }
222 else {
223 sizehints->flags |= PMinSize | PMaxSize;
224 sizehints->min_width = sizehints->max_width = config.dockW;
225 sizehints->min_height = sizehints->max_height = config.dockH;
226 }
227 sizehints->width = config.dockW;
228 sizehints->height = config.dockH;
229 XSetWMNormalHints (display, dockWin, sizehints);
230 XFree (sizehints);
231
232 /* Ed Goforth <e.goforth@computer.org> */
233 /* This allows window managers (such as WindowMaker) to pick up the */
234 /* command-line arguments that were used when the app was launched. */
235 /* This is particularly useful when docking. */
236 XSetCommand (display, dockWin, argv, argc);
237 XSetCommand (display, dockIconWin, argv, argc);
238
239 #define INPUT_MASK ButtonPressMask | ButtonReleaseMask | ExposureMask | EnterWindowMask | LeaveWindowMask | StructureNotifyMask | SubstructureNotifyMask
240 XSelectInput (display, dockWin, INPUT_MASK);
241 XSelectInput (display, dockIconWin, INPUT_MASK);
242 #undef INPUT_MASK
243 }
244
setDockIcon(void)245 static void setDockIcon (void) {
246 Pixmap dockIconPix, dockIconMask, icon;
247 int dockIconW, dockIconH;
248 unsigned int dx, dy, res = 0;
249 GC gc;
250 XGCValues xgcv;
251
252 if (useDefaultDockIcon == 0)
253 res = createPixmapFromFile (config.dockIcon, &dockIconPix, &dockIconMask,
254 &dockIconW, &dockIconH, config.dockW, config.dockH,
255 RESIZE_SMALLER);
256 if (useDefaultDockIcon == 1 || res == 0)
257 createPixmapFromData ((const char **)defaultDockIcon_xpm, &dockIconPix,
258 &dockIconMask, &dockIconW, &dockIconH,
259 config.dockW, config.dockH, RESIZE_SMALLER);
260
261 dx = (config.dockW - dockIconW) / 2;
262 dy = (config.dockH - dockIconH) / 2;
263
264 XShapeCombineMask (display, dockIconWin,
265 ShapeBounding, dx, dy, dockIconMask, ShapeSet);
266 XShapeCombineMask (display, dockWin,
267 ShapeBounding, dx, dy, dockIconMask, ShapeSet);
268
269 icon = XCreatePixmap (display, dockWin, config.dockW, config.dockH,
270 (unsigned int)DefaultDepth (display, 0));
271 xgcv.clip_x_origin = dx;
272 xgcv.clip_y_origin = dy;
273 xgcv.clip_mask = dockIconMask; /* could be omitted */
274 xgcv.graphics_exposures = False;
275 gc = XCreateGC (display, dockWin,
276 GCClipMask|GCClipXOrigin|GCClipYOrigin|GCGraphicsExposures,
277 &xgcv);
278 XCopyArea (display, dockIconPix, icon, gc, 0, 0,
279 dockIconW, dockIconH, dx, dy);
280
281 XSetWindowBackgroundPixmap (display, dockWin, icon);
282 XSetWindowBackgroundPixmap (display, dockIconWin, icon);
283 if (icon != None) XFreePixmap (display, icon);
284 XFreeGC (display, gc);
285
286 if (dockIconPix != None) XFreePixmap (display, dockIconPix);
287 if (dockIconMask != None) XFreePixmap (display, dockIconMask);
288
289 XMapWindow (display, dockWin);
290 XClearWindow (display, dockWin);
291 XClearWindow (display, dockIconWin);
292
293 XFlush (display);
294 }
295
getDockPosition(void)296 static void getDockPosition (void) {
297 Window win, frame, parent, root, *children;
298 unsigned int nchildren;
299 XWindowAttributes waP, waC;
300
301 dpyWidth = DisplayWidth (display, XDefaultScreen (display));
302 dpyHeight = DisplayHeight (display, XDefaultScreen (display));
303
304 if (config.windowedMode == 1) {
305 win = dockWin;
306 }
307 else {
308 win = dockIconWin;
309 }
310 /* get frame win */
311 root = 0;
312 frame = parent = win;
313 while (parent != root) {
314 frame = parent;
315 XQueryTree (display, frame, &root, &parent, &children, &nchildren);
316 dbg_msg (1, "Parent win: 0x%x\n", parent);
317 if (children != None) XFree (children);
318 }
319
320 XGetWindowAttributes (display, frame, &waP);
321 XGetWindowAttributes (display, win, &waC);
322 dockX = waP.x + waC.x;
323 dockY = waP.y + waC.y;
324 dbg_msg (1, "Dock position: %d, %d\n", dockX, dockY);
325 }
326
getDrawerPosition(int * x,int * y)327 static void getDrawerPosition (int *x, int *y) {
328 getDockPosition ();
329 switch (config.direction) {
330 case topToBottom:
331 *x = dockX - (int) (drawerW + config.borderSize*2 - config.dockW) / 2;
332 *y = dockY + config.dockH;
333 break;
334 case rightToLeft:
335 *x = dockX - (drawerW + config.borderSize*2);
336 *y = dockY - (int) (drawerH + config.borderSize*2 - config.dockH) / 2;
337 break;
338 case bottomToTop:
339 *x = dockX - (int) (drawerW + config.borderSize*2 - config.dockW) / 2;
340 *y = dockY - (drawerH + config.borderSize*2);
341 break;
342 case leftToRight:
343 *x = dockX + config.dockW;
344 *y = dockY - (int) (drawerH + config.borderSize*2 - config.dockH) / 2;
345 break;
346 }
347 if (*x < 0) *x = 0;
348 if (*x + drawerW + config.borderSize*2 > dpyWidth - 1) {
349 *x = dpyWidth - (drawerW + config.borderSize*2);
350 }
351 if (*y < 0) *y = 0;
352 if (*y + drawerH + config.borderSize*2 > dpyHeight - 1) {
353 *y = dpyHeight - (drawerH + config.borderSize*2);
354 }
355 }
356
showDrawer(void)357 static void showDrawer (void) {
358 int x, y;
359 unsigned int dw, dh, inc;
360
361 if (!drawerOK)
362 return;
363 drawerOpened = 1;
364 highlightCol = highlightRow = -1;
365 getDrawerPosition(&x, &y);
366
367 // if no animation for drawer
368 if (config.animationSpeed == 0) {
369 XMoveResizeWindow (display, drawer.win, x, y, drawerW, drawerH);
370 XMapRaised (display, drawer.win);
371 return;
372 }
373
374 inc = (nbRows * btnDim) / ((5 - config.animationSpeed) * 4);
375 switch (config.direction) {
376 case topToBottom:
377 for (dh = inc; dh < drawerH+inc; dh = dh+inc) {
378 if (dh >= drawerH) {
379 XMoveResizeWindow (display, drawer.win, x, y, drawerW, drawerH);
380 }
381 else {
382 XMoveResizeWindow (display, drawer.win, x, y, drawerW, dh);
383 }
384 XMapRaised (display, drawer.win);
385 XClearWindow (display, subDrawer.win);
386 XSync (display, 0);
387 mySleep (15);
388 }
389 break;
390 case rightToLeft:
391 for (dw = inc; dw < drawerW+inc; dw = dw+inc) {
392 if (dw >= drawerW) {
393 XMoveResizeWindow (display, drawer.win, x, y, drawerW, drawerH);
394 }
395 else {
396 XMoveResizeWindow (display, drawer.win,
397 dockX - dw - config.borderSize*2, y, dw, drawerH);
398 }
399 XMapRaised (display, drawer.win);
400 XClearWindow (display, subDrawer.win);
401 XSync (display, 0);
402 mySleep (15);
403 }
404 break;
405 case bottomToTop:
406 for (dh = inc; dh < drawerH+inc; dh = dh+inc) {
407 if (dh >= drawerH) {
408 XMoveResizeWindow (display, drawer.win, x, y, drawerW, drawerH);
409 }
410 else {
411 XMoveResizeWindow (display, drawer.win,
412 x, dockY - dh - config.borderSize*2, drawerW, dh);
413 }
414 XMapRaised (display, drawer.win);
415 XClearWindow (display, subDrawer.win);
416 XSync (display, 0);
417 mySleep (15);
418 }
419 break;
420 case leftToRight:
421 for (dw = inc; dw < drawerW+inc; dw = dw+inc) {
422 if (dw >= drawerW) {
423 XMoveResizeWindow (display, drawer.win, x, y, drawerW, drawerH);
424 }
425 else {
426 XMoveResizeWindow (display, drawer.win, x, y, dw, drawerH);
427 }
428 XMapRaised (display, drawer.win);
429 XClearWindow (display, subDrawer.win);
430 XSync (display, 0);
431 mySleep (15);
432 }
433 break;
434 default:
435 XMapWindow (display, drawer.win);
436 }
437 }
438
hideDrawer(void)439 static void hideDrawer (void) {
440 int x, y;
441 unsigned int dw, dh, inc;
442
443 if (!drawerOK)
444 return;
445 drawerOpened = 0;
446 XUnmapWindow (display, tooltip.win);
447
448 // if no animation for drawer
449 if (config.animationSpeed == 0) {
450 XUnmapWindow (display, drawer.win);
451 return;
452 }
453
454 inc = (nbRows * btnDim) / ((5 - config.animationSpeed) * 4);
455 switch (config.direction) {
456 case topToBottom:
457 x = dockX - (int) (drawerW + config.borderSize*2 - config.dockW) / 2;
458 if (x + drawerW + config.borderSize*2 > dpyWidth - 1) {
459 x = dpyWidth - (drawerW + config.borderSize*2);
460 }
461 if (x < 0) x = 0;
462 for (dh = drawerH - inc; dh >= inc; dh=dh-inc) {
463 XMoveResizeWindow (display, drawer.win,
464 x, dockY + config.dockH, drawerW, dh);
465 XMapRaised (display, drawer.win);
466 XSync (display, 0);
467 mySleep (15);
468 }
469 break;
470 case rightToLeft:
471 y = dockY - (int) (drawerH + config.borderSize*2 - config.dockH) / 2;
472 if (y + drawerH + config.borderSize*2 > dpyHeight - 1) {
473 y = dpyHeight - (drawerH + config.borderSize*2);
474 }
475 if (y < 0) y = 0;
476 for (dw = drawerW - inc; dw >= inc; dw=dw-inc) {
477 XMoveResizeWindow (display, drawer.win,
478 dockX - dw - config.borderSize*2, y, dw, drawerH);
479 XMapRaised (display, drawer.win);
480 XSync (display, 0);
481 mySleep (15);
482 }
483 break;
484 case bottomToTop:
485 x = dockX - (int) (drawerW + config.borderSize*2 - config.dockW) / 2;
486 if (x + drawerW + config.borderSize*2 > dpyWidth - 1) {
487 x = dpyWidth - (drawerW + config.borderSize*2);
488 }
489 if (x < 0) x = 0;
490 for (dh = drawerH - inc; dh >= inc; dh = dh-inc) {
491 XMoveResizeWindow (display, drawer.win,
492 x, dockY - dh - config.borderSize*2, drawerW, dh);
493 XMapRaised (display, drawer.win);
494 XSync (display, 0);
495 mySleep (15);
496 }
497 break;
498 case leftToRight:
499 y = dockY - (int) (drawerH + config.borderSize*2 - config.dockH) / 2;
500 if (y + drawerH + config.borderSize*2 > dpyHeight - 1) {
501 y = dpyHeight - (drawerH + config.borderSize*2);
502 }
503 if (y < 0) y = 0;
504 for (dw = drawerW - inc; dw >= inc; dw=dw-inc) {
505 XMoveResizeWindow (display, drawer.win,
506 dockX + config.dockW, y, dw, drawerH);
507 XMapRaised (display, drawer.win);
508 XSync (display, 0);
509 mySleep (15);
510 }
511 break;
512 }
513 XUnmapWindow (display, drawer.win);
514 }
515
buildDrawer(void)516 static void buildDrawer (void) {
517 /* XColor cursor_color; */
518 unsigned int res = 0, x, y, xr, yr, row, col;
519 int pixmapW, pixmapH;
520 unsigned long resizeMask;
521
522 Pixmap bgImgPix, bgImgMask;
523 Pixmap iconPix, iconMask;
524
525 if (!drawerOK)
526 return;
527 btns = config.entries;
528
529 resizeMask = RESIZE_SMALLER;
530 if (config.iconsExpand == 1)
531 resizeMask |= RESIZE_GREATER;
532
533 if (config.btnsSize == 0) {
534 btnDim = config.dockW / nbCols;
535 if (btnDim < MIN_ICON_SIZE) btnDim = MIN_ICON_SIZE;
536 }
537 else {
538 btnDim = config.btnsSize;
539 }
540
541 if (config.direction == leftToRight || config.direction == rightToLeft) {
542 drawerW = nbRows * btnDim;
543 drawerH = nbCols * btnDim;
544 }
545 else {
546 drawerW = nbCols * btnDim;
547 drawerH = nbRows * btnDim;
548 }
549 dbg_msg(1, "drawerW = %d, drawerH = %d, btn dim = %d\n",
550 drawerW, drawerH, btnDim);
551
552 /* create drawer's root window */
553 drawer.win = XCreateSimpleWindow (display, DefaultRootWindow (display),
554 0, 0, drawerW, drawerH,
555 config.borderSize, 0, 0);
556 drawer.xswa.event_mask = EnterWindowMask | LeaveWindowMask;
557 drawer.xswa.override_redirect = True;
558 XChangeWindowAttributes (display, drawer.win,
559 CWEventMask | CWOverrideRedirect, &(drawer.xswa));
560 if (config.transparency == 1) {
561 XSetWindowBackgroundPixmap (display, drawer.win, ParentRelative);
562 }
563
564 /* create drawer's subwindow */
565 subDrawer.win = XCreateSimpleWindow (display, drawer.win,
566 0, 0, drawerW, drawerH,
567 0, 0, 0);
568 subDrawer.xswa.event_mask = ButtonPressMask|ButtonReleaseMask|PointerMotionMask;
569 subDrawer.xswa.override_redirect = False;
570 XChangeWindowAttributes (display, subDrawer.win,
571 CWEventMask | CWOverrideRedirect, &(subDrawer.xswa));
572 if (config.transparency == 1) {
573 XSetWindowBackgroundPixmap (display, subDrawer.win, ParentRelative);
574 }
575 XMapWindow (display, subDrawer.win);
576
577 /* highlight mask pixmap */
578 if (useDefaultHighlightImg == 0) {
579 res = createPixmapFromFile (config.highlightImg,
580 &highlightImgPix, &highlightImgMask,
581 &pixmapW, &pixmapH, btnDim, btnDim, resizeMask);
582 }
583 if (useDefaultHighlightImg == 1 || res == 0) {
584 createPixmapFromData ((const char **)defaultHighlightImg_xpm,
585 &highlightImgPix, &highlightImgMask,
586 &pixmapW, &pixmapH, btnDim, btnDim, resizeMask);
587 }
588
589 /* btn's background pixmap */
590 if (useDefaultIconsBg == 0) {
591 res = createPixmapFromFile (config.iconsBg, &bgImgPix, &bgImgMask,
592 &pixmapW, &pixmapH, btnDim, btnDim, resizeMask);
593 }
594 if (useDefaultIconsBg == 1 || res == 0) {
595 createPixmapFromData ((const char **)defaultIconsBg_xpm,
596 &bgImgPix, &bgImgMask,
597 &pixmapW, &pixmapH, btnDim, btnDim, resizeMask);
598 }
599
600 /* Create boutons windows and set bg */
601 for (col=0; col<nbCols; col++) {
602 for (row=0; row<nbRows; row++) {
603 if (config.direction == leftToRight || config.direction == rightToLeft) {
604 x = row * btnDim;
605 y = col * btnDim;
606 }
607 else {
608 x = col * btnDim;
609 y = row * btnDim;
610 }
611 btns[col][row].da.win = XCreateSimpleWindow (display, subDrawer.win,
612 x, y, btnDim, btnDim,
613 0, 0, 0);
614 btns[col][row].da.pixmap = XCreatePixmap (display, btns[col][row].da.win,
615 btnDim, btnDim,
616 DefaultDepth (display, 0));
617 btns[col][row].da.gc = XCreateGC (display, btns[col][row].da.pixmap, 0,
618 &(btns[col][row].da.xgcv));
619 if (config.transparency == 0) {
620 XCopyArea (display, bgImgPix, btns[col][row].da.pixmap,
621 btns[col][row].da.gc,
622 0, 0, btnDim, btnDim, 0, 0);
623 }
624 }
625 }
626 if (bgImgPix != None) XFreePixmap (display, bgImgPix);
627 if (bgImgMask != None) XFreePixmap (display, bgImgMask);
628
629 /* Draw icons */
630 for (col=0; col<nbCols; col++) {
631 for (row=0; row<nbRows; row++) {
632 if (btns[col][row].isEmpty == 1) continue;
633 res = createPixmapFromFile (btns[col][row].image, &iconPix, &iconMask,
634 &pixmapW, &pixmapH, btnDim, btnDim,
635 resizeMask);
636 if (res == 0)
637 createPixmapFromData ((const char **)defaultIcon_xpm,
638 &iconPix, &iconMask,
639 &pixmapW, &pixmapH, btnDim, btnDim, resizeMask);
640 xr = (btnDim - pixmapW) / 2;
641 yr = (btnDim - pixmapH) / 2;
642 if (config.transparency == 1) {
643 XShapeCombineMask (display, btns[col][row].da.win, ShapeBounding, xr, yr,
644 iconMask, ShapeSet);
645 }
646 btns[col][row].da.xgcv.clip_x_origin = xr;
647 btns[col][row].da.xgcv.clip_y_origin = yr;
648 btns[col][row].da.xgcv.clip_mask = iconMask;
649 XChangeGC (display, btns[col][row].da.gc,
650 GCClipXOrigin | GCClipYOrigin | GCClipMask,
651 &(btns[col][row].da.xgcv));
652 XCopyArea (display, iconPix, btns[col][row].da.pixmap,
653 btns[col][row].da.gc, 0, 0, btnDim, btnDim, xr, yr);
654 }
655 }
656 if (iconPix != None) XFreePixmap (display, iconPix);
657 if (iconMask != None) XFreePixmap (display, iconMask);
658
659 for (col=0; col<nbCols; col++) {
660 for (row=0; row<nbRows; row++) {
661 if (btns[col][row].isEmpty == 1 && config.transparency == 1) continue;
662 btns[col][row].da.xswa.event_mask = 0;
663 btns[col][row].da.xswa.background_pixmap = btns[col][row].da.pixmap;
664 btns[col][row].da.xswa.override_redirect = False;
665 XChangeWindowAttributes (display, btns[col][row].da.win,
666 CWEventMask | CWBackPixmap | CWOverrideRedirect,
667 &(btns[col][row].da.xswa));
668 XMapWindow (display, btns[col][row].da.win);
669 }
670 }
671
672 /* Cursor */
673 cursor = XCreateFontCursor (display, config.cursor * 2);
674 /* Red cursor, just for fun ? */
675 /* cursor_color.red = 65535; */
676 /* cursor_color.green = 0; */
677 /* cursor_color.blue = 0; */
678 /* XRecolorCursor(display, cursor, &cursor_color, */
679 /* &WhitePixel (display, DefaultScreen (display))); */
680 XDefineCursor (display, drawer.win, cursor);
681 XFreeCursor (display, cursor);
682 }
683
buildTooltip(void)684 static void buildTooltip (void) {
685 /* load font */
686 if ((font = XLoadQueryFont (display, config.tooltipsFont)) == NULL) {
687 printf("%s warning: cannot open font \"%s\" for tooltips\n",
688 PACKAGE, config.tooltipsFont);
689 return;
690 }
691 /* build window */
692 tooltip.win = XCreateSimpleWindow (display, RootWindow(display, 0),
693 0, 0, 1, 1, 1, 0,
694 WhitePixel (display,
695 DefaultScreen (display)));
696 tooltip.gc = XCreateGC (display, tooltip.win, 0, &(tooltip.xgcv));
697 XSetFont (display, tooltip.gc, font->fid);
698 XSetForeground (display, tooltip.gc,
699 BlackPixel (display, DefaultScreen (display)));
700 XSetBackground (display, tooltip.gc,
701 WhitePixel (display, DefaultScreen (display)));
702 tooltip.xswa.event_mask = 0;
703 tooltip.xswa.override_redirect = True;
704 XChangeWindowAttributes (display, tooltip.win,
705 CWEventMask | CWOverrideRedirect, &(tooltip.xswa));
706 }
707
highlightBtn(int col,int row)708 static void highlightBtn (int col, int row) {
709 /* if transparency = 1 (pseudo transparency), only highlight shading works */
710 if (config.transparency == 1 && config.highlight == 1) {
711 config.highlight = 2;
712 }
713 switch (config.highlight) {
714 case 1:
715 highlightBtnTarget (col, row);
716 break;
717 case 2:
718 highlightBtnShading (col, row);
719 break;
720 }
721 }
722
highlightBtnShading(int col,int row)723 static void highlightBtnShading (int col, int row) {
724 XImage *img;
725 ShadingInfo shade;
726
727 img = XGetImage (display, btns[col][row].da.win,
728 0, 0, btnDim, btnDim, AllPlanes, ZPixmap);
729 shade.shading = config.highlightShading.shading;
730 shade.tintColor.red = config.highlightShading.tintColor.red * 256;
731 shade.tintColor.green = config.highlightShading.tintColor.green * 256;
732 shade.tintColor.blue = config.highlightShading.tintColor.blue * 256;
733 ShadeXImage (display, img, &shade);
734 /* gc is OK, nothing to do */
735 XPutImage (display, btns[col][row].da.win,
736 btns[col][row].da.gc, img, 0, 0, 0, 0, btnDim, btnDim);
737 XDestroyImage (img);
738 if( !XCopyArea (display, btns[col][row].da.win, btns[col][row].da.win,
739 btns[col][row].da.gc, 0, 0, btnDim, btnDim, 0, 0)) {
740 XFillRectangle ( display, btns[col][row].da.win, btns[col][row].da.gc,
741 0, 0, btnDim, btnDim);
742 }
743 }
744
highlightBtnTarget(int col,int row)745 static void highlightBtnTarget (int col, int row) {
746 XGCValues xgcv;
747 GC highlightGC;
748
749 xgcv.clip_mask = highlightImgMask;
750 highlightGC = XCreateGC (display, btns[col][row].da.win, GCClipMask, &xgcv);
751 XCopyArea (display, highlightImgPix, btns[col][row].da.win,
752 highlightGC, 0, 0, btnDim, btnDim, 0, 0);
753 XFreeGC (display, highlightGC);
754 }
755
unhighlightBtn(Window btnWin)756 static void unhighlightBtn (Window btnWin) {
757 //XClearArea (display, btnWin, 0, 0, btnDim, btnDim, False);
758 XClearWindow (display, btnWin);
759 }
760
rebuildApp(void)761 static void rebuildApp (void) {
762 if (configChanged ()) {
763 if (drawerOpened == 1) hideDrawer ();
764 /* restore mouse cursor */
765 XUndefineCursor (display, drawer.win);
766 xfreeAllMemory ();
767 freeAllMemory ();
768
769 parseConfig ();
770 XResizeWindow(display, dockWin, config.dockW, config.dockH);
771 XResizeWindow(display, dockIconWin, config.dockW, config.dockH);
772 setDockIcon ();
773 buildDrawer ();
774 buildTooltip ();
775 }
776 }
777
execCmd(const char * cmd)778 void execCmd (const char *cmd) {
779 int cpid;
780
781 cpid = fork ();
782 if (cpid == -1) {
783 printf ("%s error: can't fork\n", PACKAGE);
784 }
785 else if (cpid == 0) {
786 setsid ();
787 execl ("/bin/sh", "/bin/sh", "-c", cmd, NULL);
788 exit (EXIT_SUCCESS);
789 }
790 }
791
792 /* msec is miliseconds, msec <= 999 */
mySleep(unsigned long msec)793 static void mySleep (unsigned long msec) {
794 struct timespec req, rem;
795
796 req.tv_sec = 0;
797 req.tv_nsec = msec * 1000000;
798
799 while (nanosleep (&req, &rem) != 0) {
800 req = rem;
801 }
802 }
803
xEnterEventOrTimeout(XEvent * e,unsigned long msec)804 static int xEnterEventOrTimeout (XEvent *e, unsigned long msec) {
805 struct timeval timeout;
806 fd_set rset;
807
808 dbg_msg (1, "Enter xNextEventOrTimeout function\n");
809
810 XSync (display, False);
811 if (XPending (display)) {
812 XNextEvent (display, e);
813 dbg_msg (1, "Event type = %d\n", e->type);
814 if (e->type == EnterNotify) return 1;
815 }
816
817 timeout.tv_sec = msec / 1000;
818 timeout.tv_usec = (msec % 1000) * 1000;
819 FD_ZERO (&rset);
820 FD_SET (ConnectionNumber (display), &rset);
821
822 if (select (ConnectionNumber (display)+1, &rset, NULL, NULL, &timeout) > 0) {
823 XNextEvent (display, e);
824 return 1;
825 }
826 return 0;
827 }
828
829 /* Convert mouse coordinate into button coordinate */
convertMCoordIntoBCoord(int mcol,int mrow,int * bcol,int * brow)830 static void convertMCoordIntoBCoord (int mcol, int mrow, int *bcol, int *brow) {
831 switch (config.direction) {
832 case topToBottom:
833 case bottomToTop:
834 *bcol = (int)((float)mcol / btnDim);
835 *brow = (int)((float)mrow / btnDim);
836 break;
837 case rightToLeft:
838 case leftToRight:
839 *bcol = (int)((float)mrow / btnDim);
840 *brow = (int)((float)mcol / btnDim);
841 break;
842 }
843 }
844
eventLoop(void)845 static void eventLoop (void) {
846 XEvent e;
847 int x, y, w ,h;
848 /* coord of btn pressed */
849 int prow = 0, pcol = 0;
850 /* btn's coord during motion */
851 int mrow = 0, mcol = 0;
852 /* coord when btn released */
853 int rrow = 0, rcol = 0;
854 /* X btn's coord */
855 int xbtn = 0, ybtn = 0;
856 unsigned int delta, btnIsPressed = 0;
857
858 while (True) {
859 XNextEvent (display, &e);
860 rebuildApp ();
861 /* look at X.h & Xlib.h in /usr/X11R6/include/X11/ */
862 switch (e.type) {
863 case Expose:
864 dbg_msg (2, "Expose event: count = %d, win = 0x%x\n",
865 e.xexpose.count, e.xexpose.window);
866 break;
867 case ReparentNotify :
868 case ConfigureNotify:
869 dbg_msg (1, "ConfigureNotify event\n");
870 if (drawerOpened == 1) {
871 getDrawerPosition (&x, &y);
872 XMoveWindow (display, drawer.win, x, y);
873 }
874 break;
875 case MotionNotify:
876 if (config.highlight == 0 || btnIsPressed == 1 || drawerOpened == 0) break;
877 convertMCoordIntoBCoord(e.xmotion.x, e.xmotion.y, &mcol, &mrow);
878 if (highlightCol == mcol && highlightRow == mrow) break;
879 dbg_msg (1, "Motion event: win=0x%x, subwin=0x%x, (%d, %d) (%d, %d)\n",
880 e.xmotion.window, e.xmotion.subwindow,
881 highlightCol, highlightRow, mcol, mrow);
882 if (mcol < 0 || mcol >= nbCols || mrow < 0 || mrow >= nbRows) break;
883 /* Ok, first raise drawer */
884 XRaiseWindow (display, drawer.win);
885 /* tooltips */
886 if (config.tooltips == 1) {
887 if (!btns[mcol][mrow].isEmpty && strlen (btns[mcol][mrow].tooltip) > 0) {
888 getDrawerPosition (&x, &y);
889 w = XTextWidth (font, btns[mcol][mrow].tooltip,
890 strlen(btns[mcol][mrow].tooltip)) + 8;
891 h = font->max_bounds.ascent + font->max_bounds.descent + 4;
892 switch (config.direction) {
893 case rightToLeft:
894 case leftToRight:
895 x = x + mrow * btnDim + btnDim / 2;
896 y = y + mcol * btnDim - h - 4;
897 break;
898 case bottomToTop:
899 case topToBottom:
900 x = x + mcol * btnDim + btnDim / 2;
901 y = y + mrow * btnDim - h - 4;
902 break;
903 }
904 if (x < 0) x = 0;
905 if (x + w + 2 > dpyWidth - 1) x = dpyWidth - (w + 2);
906 XMoveResizeWindow (display, tooltip.win, x, y, w, h);
907 XMapRaised (display, tooltip.win);
908 XClearWindow (display, tooltip.win);
909 XDrawString (display, tooltip.win, tooltip.gc,
910 0 + 4, font->max_bounds.ascent + font->max_bounds.descent,
911 btns[mcol][mrow].tooltip,
912 strlen(btns[mcol][mrow].tooltip));
913 }
914 else {
915 XUnmapWindow (display, tooltip.win);
916 }
917 }
918 /* highlight */
919 if (highlightCol >= 0 && highlightRow >= 0) {
920 if (btns[highlightCol][highlightRow].isEmpty == 0) {
921 unhighlightBtn (btns[highlightCol][highlightRow].da.win);
922 }
923 }
924 if (btns[mcol][mrow].isEmpty == 0) {
925 highlightBtn (mcol, mrow);
926 highlightCol = mcol;
927 highlightRow = mrow;
928 }
929 else highlightCol = highlightRow = -1;
930 break;
931 case ButtonPress:
932 dbg_msg (1, "Button press event: win = 0x%x, cursor = (%d,%d)\n",
933 e.xbutton.window, e.xbutton.y, e.xbutton.y);
934 if (drawerOpened == 0) {
935 showDrawer ();
936 }
937 else if (drawerOpened == 1 && e.xbutton.window != subDrawer.win) {
938 hideDrawer ();
939 }
940 else if (drawerOpened == 1 && e.xbutton.window == subDrawer.win) {
941 xbtn = e.xbutton.x / btnDim;
942 ybtn = e.xbutton.y / btnDim;
943 convertMCoordIntoBCoord (e.xbutton.x, e.xbutton.y, &pcol, &prow);
944 delta = 1;
945 if (btns[pcol][prow].isEmpty == 0) {
946 btnIsPressed = 1;
947 XMoveWindow (display, btns[pcol][prow].da.win,
948 xbtn * btnDim + delta, ybtn * btnDim + delta);
949 XMapRaised (display, btns[pcol][prow].da.win);
950 XSync (display, 0);
951 }
952 }
953 break;
954 case ButtonRelease:
955 dbg_msg (1, "Button release event: win = 0x%x, cursor = (%d,%d)\n",
956 e.xbutton.window, e.xbutton.y, e.xbutton.y);
957 btnIsPressed = 0;
958 if (drawerOpened == 1 && e.xbutton.window == subDrawer.win) {
959 convertMCoordIntoBCoord(e.xbutton.x, e.xbutton.y, &rcol, &rrow);
960 if (btns[pcol][prow].isEmpty == 0) {
961 XMoveWindow (display, btns[pcol][prow].da.win,
962 xbtn * btnDim, ybtn * btnDim);
963 XMapRaised (display, btns[pcol][prow].da.win);
964 XSync (display, False);
965 }
966 if (prow == rrow && pcol == rcol && btns[pcol][prow].isEmpty == 0) {
967 /* if release coord = press coord => OK exec cmd */
968 dbg_msg (1, "Command executed : %s (%d, %d)\n",
969 btns[pcol][prow].command, pcol, prow);
970 execCmd (btns[pcol][prow].command);
971 if ((e.xbutton.state & ShiftMask) != 0 || e.xbutton.button != 1) {
972 /* If Shift is down whilst selecting app keep the drawer open */
973 /* or mouse button != 1 => nothing */
974 }
975 else {
976 XUnmapWindow (display, tooltip.win);
977 mySleep (500);
978 hideDrawer ();
979 }
980 }
981 }
982 break;
983 case EnterNotify:
984 dbg_msg (1, "Enter window: 0x%x 0x%x, mode=%d, detail=%d\n",
985 (unsigned int)e.xcrossing.window,
986 drawer.win, e.xcrossing.mode, e.xcrossing.detail);
987 /* mode != 2 to avoid EnterNotify event emit after ButtonRelease
988 in windowed mode */
989 if (drawerOpened == 0 && config.showOnHover == 1 && e.xcrossing.mode != 2) {
990 showDrawer ();
991 }
992 break;
993 case LeaveNotify:
994 dbg_msg (1, "Leave window: win=0x%x, mode=%d, detail=%d\n",
995 (unsigned int)e.xcrossing.window, e.xcrossing.mode,
996 e.xcrossing.detail);
997 /* if we leave drawer => unhighlight the last button highlighted and
998 unmap tooltip window */
999 if (config.highlight > 0 && btnIsPressed == 0 && drawerOpened == 1 &&
1000 e.xcrossing.window == drawer.win &&
1001 highlightCol >= 0 && highlightRow >= 0) {
1002 XUnmapWindow (display, tooltip.win);
1003 unhighlightBtn (btns[highlightCol][highlightRow].da.win);
1004 highlightCol = highlightRow = -1;
1005 }
1006 /* mode = NotifyNormal:0, NotifyGrab:1, NotifyUngrab:2, NotifyWhileGrabbed:3
1007 mode != 1 to avoid LeaveNotify event emit before ButtonPress
1008 in windowed mode */
1009 if (drawerOpened == 1 && config.hideOnOut == 1 && e.xcrossing.mode != 1) {
1010 if (! xEnterEventOrTimeout (&e, config.hideTimeout)) {
1011 hideDrawer ();
1012 }
1013 }
1014 break;
1015 case DestroyNotify:
1016 quit (EXIT_SUCCESS);
1017 }
1018 }
1019 }
1020