wmbutton: Middle button enabled by default
[dockapps.git] / wmbutton / wmb_libs.c
blob8f54d5e1d7eea301be7fd0fc9b6c6bb4de0c31a0
1 /* wmb_libs.c - Edward H. Flora - ehf_dockapps@cox.net */
2 /* Last Modified: 4/3/04 */
3 /*
4 * These functions are designed to work with wmbutton.
5 */
7 /* PORTABILITY:
8 ******************
9 * Coded in ANSI C, using ANSI prototypes.
12 /****** Include Files *************************************************/
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include "wmbutton.h"
19 /****** ToolTip Globals ***********************************************/
21 static struct timeval _tStart;
23 extern struct Config_t Config;
24 int _bTooltip = 0;
25 XFontStruct* _fTooltip;
26 int _nFontHeight, _nFontY;
27 int _nScreenWidth, _nScreenHeight;
28 GC _gcMono= 0;
29 Window _wTooltip;
31 /****** Parse Command Line ********************************************/
32 void parseargs(int argc, char **argv) {
33 int current;
34 char *Home = getenv("HOME");
36 while (-1 != (current = getopt(argc, argv, "vhnmsF:b:g:d:f:"))) {
37 switch (current) {
38 case 'v':
39 Config.Verbose = 1;
40 break;
41 case '?':
42 case 'h':
43 show_usage();
44 break;
45 case 'm':
46 Config.mmouse = 1;
47 break;
48 case 'n':
49 Config.bTooltipDisable = 1;
50 break;
51 case 's':
52 Config.bTooltipSwapColors = 1;
53 break;
54 case 'g':
55 Config.Geometry_str = strdup(optarg);
56 break;
57 case 'd':
58 Config.Display_str = strdup(optarg);
59 break;
60 case 'f':
61 Config.configfile = strdup(optarg);
62 break;
63 case 'F':
64 Config.szTooltipFont = strdup(optarg);
65 break;
66 case 'b':
67 Config.buttonfile = strdup(optarg);
68 break;
72 if (!Config.configfile) {
73 if (Home != NULL) {
74 Config.configfile = malloc(
75 strlen(Home) + strlen(CONFFILENAME) + 1);
76 sprintf(Config.configfile, "%s%s", Home, CONFFILENAME);
80 if (!Config.buttonfile) {
81 if (Home != NULL) {
82 Config.buttonfile = malloc(
83 strlen(Home) + strlen(BUTTONFILENAME) + 1);
84 sprintf(Config.buttonfile, "%s%s", Home, BUTTONFILENAME);
88 if (!Config.Geometry_str)
89 Config.Geometry_str = "64x64+0+0";
91 if (!Config.Display_str)
92 Config.Display_str = "";
94 if (!Config.szTooltipFont)
95 Config.szTooltipFont = TOOLTIP_FONT;
97 if (!Config.bTooltipDisable)
98 Config.bTooltipDisable = !TOOLTIP_SUPPORT;
102 /****** Show Usage Information ****************************************/
103 void show_usage() {
104 extern char *app_name;
106 fprintf(stderr,"\n");
107 fprintf(stderr,"usage: %s [-g geom] [-d dpy] [-f cfgfile] [-b btnfile] "\
108 "[-F <font>] [-v] [-s] [-n]\n",app_name);
109 fprintf(stderr,"\n");
110 fprintf(stderr," wmbutton version %s\n", VER_STR);
111 fprintf(stderr,"\n");
112 fprintf(stderr,"-g <geometry> Window Geometry - ie: 64x64+10+10\n");
113 fprintf(stderr,"-d <display> Display - ie: 127.0.0.1:0.0\n");
114 fprintf(stderr,"-f <filename> Full path to configuration file.\n");
115 fprintf(stderr,"-b <filename> Full path to button xpm.\n");
116 fprintf(stderr,"-F <font> Custom tooltip font (e.g. -b\\&h-lucidatypewriter-medium-*-*-*-12-*)\n");
117 fprintf(stderr,"-v Verbose Mode.\n");
118 fprintf(stderr,"-h Help. This message.\n");
119 fprintf(stderr,"-m Disable Middle Mouse functionality.\n");
120 fprintf(stderr,"-s Swap tooltip colors.\n");
121 fprintf(stderr,"-n Turn off tooltips.\n");
122 fprintf(stderr,"\n");
123 exit(0);
124 }/***********************************************************************/
127 /****** Error Handler Routine *****************************************/
128 void err_mess(int err, char *str) {
130 switch (err) {
131 case FAILDISP:
132 fprintf(stderr,"Fail: XOpenDisplay for %s\n", str);
133 exit(err);
134 case FAILSWIN:
135 fprintf(stderr,"Fail: XCreateSimpleWindow\n");
136 exit(err);
137 case FAILICON:
138 fprintf(stderr,"Fail: XCreateSimpleWindow\n");
139 exit(err);
140 case FAILXPM:
141 fprintf(stderr,"Fail: XCreateBitmapFromData\n");
142 break;
143 case FAILWNAM:
144 fprintf(stderr,"%s: Can't set up window name\n", str);
145 exit(err);
146 case FAILGC:
147 fprintf(stderr,"Fail: XCreateGC\n");
148 exit(err);
149 case FAILCONF:
150 fprintf(stderr, "Fail: Can't Find user or system configuration file.\n");
151 fprintf(stderr, "Fail: User Config: '%s'\n", str);
152 fprintf(stderr, "Fail: System Config: '%s'\n", CONFIGGLOBAL);
153 exit(err);
154 case FAILTMPL:
155 fprintf(stderr, "Fail: Can't Create 'template' Pixmap\n");
156 exit(err);
157 case FAILVIS:
158 fprintf(stderr, "Fail: Can't Create 'visible' Pixmap\n");
159 exit(err);
160 case FAILBUT:
161 fprintf(stderr, "Fail: Can't Create 'buttons' Pixmap\n");
162 exit(err);
163 default:
164 fprintf(stderr, "Fail: UnSpecified Error: %d\n",err);
165 fprintf(stderr, "Fail: %s\n",str);
166 exit(err);
169 }/***********************************************************************/
171 /***********************************************************************
172 * RunAppN(int app)
174 * Run the command given in the configuration file 'configfile'
175 ***********************************************************************/
176 void RunAppN( int app ) {
177 char *cmndstr;
178 extern struct Config_t Config;
180 cmndstr = Parse(app); // Get command to pass to system
182 if (Config.Verbose) fprintf(stderr, "Command String: %s", cmndstr);
184 if (cmndstr != NULL) {
185 system(cmndstr); // if there's a command, run it
186 free(cmndstr);
188 }/***********************************************************************/
190 /***********************************************************************
191 * Parse(int app)
193 * Parses the file 'configfile' for command to execute.
194 ***********************************************************************/
195 char *Parse(int app) {
196 FILE *fp;
197 char Buf[BUFFER_SIZE];
198 char *Ptr;
200 if ((fp = fopen(Config.configfile, "r")) == NULL)
201 if ((fp = fopen(CONFIGGLOBAL, "r")) == NULL)
202 err_mess(FAILCONF,Config.configfile);
204 while ((Ptr = fgets(Buf, BUFFER_SIZE, fp))) {
205 if (atoi(Buf) == app)
206 break;
209 fclose(fp);
211 if (!Ptr)
212 return Ptr;
214 Ptr = strchr(Buf, '\t'); // find first tab
215 if (Ptr == NULL) Ptr = strchr(Buf, ' '); // or space charater
216 if (Ptr == NULL) return(NULL);
217 Ptr++;
219 Ptr = strdup(Ptr);
221 return(Ptr);
222 }/**********************************************************************/
224 /***********************************************************************
225 * initTime
227 * Copyright (c) 2001 Bruno Essmann <essmann@users.sourceforge.net>
228 ***********************************************************************/
230 void initTime () {
231 extern struct Config_t Config;
233 if (Config.Verbose) {
234 fprintf(stdout, "[ ] initializing time\n");
237 gettimeofday(&_tStart, NULL);
238 }/**********************************************************************/
240 long currentTimeMillis () {
241 struct timeval tNow;
242 struct timeval tElapsed;
244 gettimeofday(&tNow, NULL);
246 if (_tStart.tv_usec > tNow.tv_usec) {
247 tNow.tv_usec+= 1000000;
248 tNow.tv_sec--;
250 tElapsed.tv_sec= tNow.tv_sec - _tStart.tv_sec;
251 tElapsed.tv_usec= tNow.tv_usec - _tStart.tv_usec;
252 return (tElapsed.tv_sec * 1000) + (tElapsed.tv_usec / 1000);
253 }/**********************************************************************/
256 void getWindowOrigin (Window w, int* nX, int* nY) {
257 extern Display *display;
258 Window wWindow, wParent, wRoot;
259 Window* wChildren;
260 unsigned int nChildren;
261 unsigned int ww, wh, wb, wd;
262 int wx, wy;
264 wParent= w;
265 do {
266 wWindow= wParent;
267 if (!XQueryTree(display, wParent, &wRoot, &wParent, &wChildren, &nChildren))
268 return;
270 if (wChildren)
271 XFree(wChildren);
273 } while (wParent != wRoot);
275 if (XGetGeometry(display, wWindow, &wRoot, &wx, &wy, &ww, &wh, &wb, &wd)) {
276 if (nX) {
277 *nX= wx;
279 if (nY) {
280 *nY= wy;
283 }/**********************************************************************/
285 /***********************************************************************
286 * getButtonLocation
288 * compute location for each button's tooltip (not perfect)
289 ***********************************************************************/
290 void getButtonLocation (int nButton, int* nLocationX, int* nLocationY) {
291 *nLocationX = 0;
292 *nLocationY = 8;
293 while (nButton > BUTTON_COLS) {
294 *nLocationY += BUTTON_SIZE;
295 nButton -= BUTTON_COLS;
297 while (nButton > 0) {
298 *nLocationX += BUTTON_SIZE - 1;
299 nButton--;
301 }/**********************************************************************/
303 /* SkipWord & SkipSpaces: utility functions for getNicenedString */
304 char *SkipWord(char *Text) {
305 char *Result = Text;
307 while ((*Result != ' ')&&(*Result != '\t')&&
308 (*Result != '\n')&&(*Result != 0x00))
309 Result++;
310 return Result;
313 char *SkipSpaces(char *Text) {
314 char *Result = Text;
316 while ((*Result == ' ')||(*Result == '\t')||(*Result == '\n'))
317 Result++;
318 return Result;
321 /***********************************************************************
322 * getNicenedString
324 * nicen the parsed command from the .wmbutton config file
325 * - cut if too long
326 * - remove parameters, whitespace and the '&'...
327 ***********************************************************************/
328 char* getNicenedString (char *old, int andAddSeparator) {
329 char *WorkStr;
330 char *WorkStrEnd;
331 char *StartPtr;
332 char *EndPtr;
334 char *RetStr;
336 if (!old) {
337 if (andAddSeparator)
338 return strdup("-- | ");
339 else
340 return strdup("--");
343 RetStr = malloc(strlen(old) + 3 + 1); // 3 for Seperator
344 *RetStr = 0x00;
346 WorkStr = strdup(old);
347 WorkStrEnd = strchr(WorkStr, 0x00);
348 StartPtr = WorkStr;
350 while(StartPtr < WorkStrEnd) {
351 StartPtr = SkipSpaces(StartPtr);
352 EndPtr = SkipWord(StartPtr);
353 *EndPtr = 0x00;
355 if ((*StartPtr == '&')||(*StartPtr == '-'))
356 break;
358 strcat(RetStr, StartPtr);
359 strcat(RetStr, " ");
360 StartPtr = EndPtr+1;
363 free(WorkStr);
365 if (andAddSeparator) {
366 strcat(RetStr, "| ");
369 return RetStr;
372 /***********************************************************************
373 * getButtonAppNames
375 * returns the 1..3 application names / commands to be shown in tooltip
376 ***********************************************************************/
377 char* getButtonAppNames (int nButton) {
378 char *str = NULL;
379 char *tmp1,*tmp2;
381 if (!( nButton < 0 || nButton > 9 )) {
383 // FIXME: _Might_ overflow, but it's unlikely.
384 // Perhaps one should fix this sometime ;)
385 str = (char*) calloc (sizeof(char), BUFFER_SIZE);
387 tmp1 = Parse(nButton + LMASK);
388 tmp2 = getNicenedString(tmp1, 1);
389 strcat(str, tmp2);
390 free(tmp1);
391 free(tmp2);
393 tmp1 = Parse(nButton + MMASK);
394 tmp2 = getNicenedString(tmp1, 1);
395 strcat(str, tmp2);
396 free(tmp1);
397 free(tmp2);
399 tmp1 = Parse(nButton + RMASK);
400 tmp2 = getNicenedString(tmp1, 0);
401 strcat(str, tmp2);
402 free(tmp1);
403 free(tmp2);
406 return(str);
407 }/**********************************************************************/
410 int hasTooltipSupport () {
411 return !Config.bTooltipDisable;
412 }/**********************************************************************/
414 void showTooltip (int nButton, int nMouseX, int nMouseY) {
415 Pixmap pixmap, mask;
416 int nMainWinX, nMainWinY;
417 int nButtonX = 0, nButtonY = 0, nButtonWidth = 0, nButtonHeight = 0;
418 int nTextY, nX, nY, nWidth, nHeight, nSide;
419 char* szText;
420 extern struct Config_t Config;
421 extern Window iconwin;
422 extern Pixel bg_pixel, fg_pixel;
423 extern Display *display;
424 extern GC gc;
426 if (Config.bTooltipDisable || nButton == -1) {
427 return;
429 if (_bTooltip) {
430 hideTooltip();
434 if (Config.Verbose) {
435 fprintf(stdout, "[%8ld] showing tooltip for button %d at %d, %d\n",
436 currentTimeMillis(),
437 nButton, nMouseX, nMouseY);
440 szText = getButtonAppNames(nButton);
441 if(!szText)
442 return;
444 _bTooltip= 1;
446 nWidth= XTextWidth(_fTooltip, szText, strlen(szText)) + 16;
447 nHeight= _nFontHeight + 4;
448 if (nHeight < 16) {
449 nHeight= 16;
451 if (nWidth < nHeight) {
452 nWidth= nHeight;
454 if (Config.Verbose) {
455 fprintf(stdout, "[%8ld] tooltip size: %d, %d\n",
456 currentTimeMillis(), nWidth, nHeight);
459 getWindowOrigin(iconwin, &nMainWinX, &nMainWinY);
460 getButtonLocation(nButton, &nButtonX, &nButtonY);
461 nButtonX+= nMainWinX;
462 nButtonY+= nMainWinY;
463 nButtonWidth = BUTTON_SIZE;
464 nButtonHeight = BUTTON_SIZE;
466 if (nButtonX + nWidth > _nScreenWidth) {
467 nSide= TOOLTIP_RIGHT;
468 nX= nButtonX - nWidth + nButtonWidth / 2;
469 if (nX < 0) {
470 nX= 0;
472 } else {
473 nSide= TOOLTIP_LEFT;
474 nX= nButtonX + nButtonWidth / 2;
476 if (nX + nWidth > _nScreenWidth) {
477 nX= _nScreenWidth - nWidth;
480 if (nButtonY - (nHeight + TOOLTIP_SPACE) < 0) {
481 nSide|= TOOLTIP_TOP;
482 nY= nButtonY + nButtonHeight - 1;
483 nTextY= TOOLTIP_SPACE;
484 } else {
485 nSide|= TOOLTIP_BOTTOM;
486 nY= nButtonY - (nHeight + TOOLTIP_SPACE);
487 nTextY= 0;
490 pixmap= createTooltipPixmap(nWidth, nHeight, nSide, &mask);
492 XSetForeground(display, gc, Config.bTooltipSwapColors ? fg_pixel : bg_pixel);
493 XSetFont(display, gc, _fTooltip->fid);
494 XDrawString(display, pixmap, gc,
495 8, nTextY + (nHeight - _nFontHeight) / 2 + _nFontY,
496 szText, strlen(szText));
498 XSetWindowBackgroundPixmap(display, _wTooltip, pixmap);
500 XResizeWindow(display, _wTooltip, nWidth, nHeight + TOOLTIP_SPACE);
501 XShapeCombineMask(display, _wTooltip, ShapeBounding, 0, 0, mask, ShapeSet);
502 XFreePixmap(display, mask);
503 XMoveWindow(display, _wTooltip, nX, nY);
504 XMapRaised(display, _wTooltip);
505 XFreePixmap(display, pixmap);
507 free(szText);
508 }/**********************************************************************/
510 void hideTooltip () {
511 extern struct Config_t Config;
512 extern Display *display;
514 if (Config.bTooltipDisable) {
515 return;
517 if (_bTooltip) {
518 if (Config.Verbose) {
519 fprintf(stdout, "[%8ld] hiding tooltip\n", currentTimeMillis());
521 XUnmapWindow(display, _wTooltip);
522 _bTooltip= 0;
524 }/**********************************************************************/
526 int hasTooltip () {
527 if (Config.bTooltipDisable) {
528 return 0;
530 return _bTooltip;
531 }/**********************************************************************/
533 void initTooltip () {
534 XSetWindowAttributes attribs;
535 unsigned long vmask;
536 extern Display *display;
537 extern char *app_name;
538 extern int screen;
539 extern Window rootwin, win;
541 if (Config.bTooltipDisable) {
542 if (Config.Verbose) {
543 fprintf(stdout, "[%8ld] initializing tooltips (disabled)\n",
544 currentTimeMillis());
547 return;
549 if (Config.Verbose) {
550 fprintf(stdout, "[%8ld] initializing tooltips\n", currentTimeMillis());
552 _fTooltip= XLoadQueryFont(display, Config.szTooltipFont);
553 if (!_fTooltip) {
554 fprintf(stderr, "%s: couldn't allocate font '%s'.\n", app_name, Config.szTooltipFont);
555 if (!strcmp(Config.szTooltipFont, TOOLTIP_FONT))
556 fprintf(stderr, "%s: Use option -F <font>\n", app_name);
557 exit(-1);
559 _nFontHeight= _fTooltip->ascent + _fTooltip->descent;
560 _nFontY= _fTooltip->ascent;
561 _nScreenWidth= WidthOfScreen(ScreenOfDisplay(display, screen));
562 _nScreenHeight= HeightOfScreen(ScreenOfDisplay(display, screen));
563 if (Config.Verbose) {
564 fprintf(stdout, "[%8ld] configuring tooltip font:\n" \
565 "[%8ld] - '%s'\n" \
566 "[%8ld] - font-height= %d, font-ascent= %d\n" \
567 "[%8ld] configuring screen size: %dx%d\n",
568 currentTimeMillis(),
569 currentTimeMillis(), Config.szTooltipFont,
570 currentTimeMillis(), _nFontHeight, _nFontY,
571 currentTimeMillis(), _nScreenWidth, _nScreenHeight );
574 vmask= CWSaveUnder | CWOverrideRedirect | CWBorderPixel;
575 attribs.save_under= True;
576 attribs.override_redirect= True;
577 attribs.border_pixel= 0;
578 _wTooltip= XCreateWindow(display, rootwin, 1, 1, 10, 10, 1,
579 CopyFromParent, CopyFromParent,
580 CopyFromParent, vmask, &attribs);
581 if (win == 0) {
582 fprintf(stderr, "Cannot create tooltip window.\n");
583 exit(-1);
585 }/**********************************************************************/
587 void destroyTooltip () {
588 extern Display *display;
590 if (Config.bTooltipDisable) {
591 return;
593 if (_gcMono) {
594 XFreeGC(display, _gcMono);
595 _gcMono= 0;
597 XDestroyWindow(display, _wTooltip);
598 }/**********************************************************************/
600 void drawTooltipBalloon (Pixmap pix, GC gc, int x, int y, int w, int h, int side) {
601 extern Display *display;
602 int rad = h*3/10;
603 XPoint pt[3];
605 XFillArc(display, pix, gc, x, y, rad, rad, 90*64, 90*64);
606 XFillArc(display, pix, gc, x, y+h-1-rad, rad, rad, 180*64, 90*64);
608 XFillArc(display, pix, gc, x+w-1-rad, y, rad, rad, 0*64, 90*64);
609 XFillArc(display, pix, gc, x+w-1-rad, y+h-1-rad, rad, rad, 270*64, 90*64);
611 XFillRectangle(display, pix, gc, x, y+rad/2, w, h-rad);
612 XFillRectangle(display, pix, gc, x+rad/2, y, w-rad, h);
614 if (side & TOOLTIP_BOTTOM) {
615 pt[0].y = y+h-1;
616 pt[1].y = y+h-1+TOOLTIP_SPACE;
617 pt[2].y = y+h-1;
618 } else {
619 pt[0].y = y;
620 pt[1].y = y-TOOLTIP_SPACE;
621 pt[2].y = y;
623 if (side & TOOLTIP_RIGHT) {
624 pt[0].x = x+w-h+2*h/16;
625 pt[1].x = x+w-h+11*h/16;
626 pt[2].x = x+w-h+7*h/16;
627 } else {
628 pt[0].x = x+h-2*h/16;
629 pt[1].x = x+h-11*h/16;
630 pt[2].x = x+h-7*h/16;
632 XFillPolygon(display, pix, gc, pt, 3, Convex, CoordModeOrigin);
633 }/**********************************************************************/
635 Pixmap createTooltipPixmap (int width, int height, int side, Pixmap *mask) {
636 extern Display *display;
637 extern GC gc;
638 extern Pixel bg_pixel, fg_pixel;
639 extern int depth;
640 extern Window rootwin;
641 Pixmap bitmap;
642 Pixmap pixmap;
643 int x, y;
645 bitmap = XCreatePixmap(display, rootwin,
646 width+TOOLTIP_SPACE, height+TOOLTIP_SPACE, 1);
648 if (!_gcMono) {
649 _gcMono= XCreateGC(display, bitmap, 0, NULL);
651 XSetForeground(display, _gcMono, 0);
652 XFillRectangle(display, bitmap, _gcMono, 0, 0,
653 width+TOOLTIP_SPACE, height+TOOLTIP_SPACE);
655 pixmap = XCreatePixmap(display, rootwin,
656 width+TOOLTIP_SPACE, height+TOOLTIP_SPACE, depth);
657 XSetForeground(display, gc, Config.bTooltipSwapColors ? fg_pixel : bg_pixel);
658 XFillRectangle(display, pixmap, gc, 0, 0,
659 width+TOOLTIP_SPACE, height+TOOLTIP_SPACE);
661 if (side & TOOLTIP_BOTTOM) {
662 y = 0;
663 } else {
664 y = TOOLTIP_SPACE;
666 x = 0;
668 XSetForeground(display, _gcMono, 1);
669 drawTooltipBalloon(bitmap, _gcMono, x, y, width, height, side);
670 XSetForeground(display, gc, Config.bTooltipSwapColors ? bg_pixel : fg_pixel);
671 drawTooltipBalloon(pixmap, gc, x+1, y+1, width-2, height-2, side);
673 *mask = bitmap;
675 return pixmap;
676 }/***********************************************************************/
679 /***********************************************************************
680 * flush_expose
682 * Everyone else has one of these... Can't hurt to throw it in.
683 ***********************************************************************/
684 int flush_expose(Window w) {
685 extern Display *display;
686 XEvent dummy;
687 int i=0;
689 while (XCheckTypedWindowEvent(display, w, Expose, &dummy)) i++;
690 return(i);
691 }/***********************************************************************/