/* wmb_libs.c - Edward H. Flora - ehf_dockapps@cox.net */ /* Last Modified: 4/3/04 */ /* * These functions are designed to work with wmbutton. */ /* PORTABILITY: ****************** * Coded in ANSI C, using ANSI prototypes. */ /****** Include Files *************************************************/ #include #include #include #include "wmbutton.h" /****** ToolTip Globals ***********************************************/ static struct timeval _tStart; extern struct Config_t Config; int _bTooltip = 0; XFontStruct* _fTooltip; int _nFontHeight, _nFontY; int _nScreenWidth, _nScreenHeight; GC _gcMono = 0; Window _wTooltip; /****** Parse Command Line ********************************************/ void parseargs(int argc, char **argv) { int current; char *Home = getenv("HOME"); while (-1 != (current = getopt(argc, argv, "vhnmsF:b:g:d:f:"))) { switch (current) { case 'v': Config.Verbose = 1; break; case '?': case 'h': show_usage(); break; case 'm': Config.mmouse = 1; break; case 'n': Config.bTooltipDisable = 1; break; case 's': Config.bTooltipSwapColors = 1; break; case 'g': Config.Geometry_str = strdup(optarg); break; case 'd': Config.Display_str = strdup(optarg); break; case 'f': Config.configfile = strdup(optarg); break; case 'F': Config.szTooltipFont = strdup(optarg); break; case 'b': Config.buttonfile = strdup(optarg); break; } } if (!Config.configfile) { if (Home != NULL) { Config.configfile = malloc(strlen(Home) + strlen(CONFFILENAME) + 1); sprintf(Config.configfile, "%s%s", Home, CONFFILENAME); } } if (!Config.buttonfile) { if (Home != NULL) { Config.buttonfile = malloc(strlen(Home) + strlen(BUTTONFILENAME) + 1); sprintf(Config.buttonfile, "%s%s", Home, BUTTONFILENAME); } } if (!Config.Geometry_str) Config.Geometry_str = "64x64+0+0"; if (!Config.Display_str) Config.Display_str = ""; if (!Config.szTooltipFont) Config.szTooltipFont = TOOLTIP_FONT; if (!Config.bTooltipDisable) Config.bTooltipDisable = !TOOLTIP_SUPPORT; } /****** Show Usage Information ****************************************/ void show_usage(void) { extern char *app_name; fprintf(stderr, "\n"); fprintf(stderr, "usage: %s [-g geom] [-d dpy] [-f cfgfile] [-b btnfile] "\ "[-F ] [-v] [-s] [-n]\n",app_name); fprintf(stderr, "\n"); fprintf(stderr, " wmbutton version %s\n", VER_STR); fprintf(stderr, "\n"); fprintf(stderr, "-g Window Geometry - ie: 64x64+10+10\n"); fprintf(stderr, "-d Display - ie: 127.0.0.1:0.0\n"); fprintf(stderr, "-f Full path to configuration file.\n"); fprintf(stderr, "-b Full path to button xpm.\n"); fprintf(stderr, "-F Custom tooltip font (e.g. -b\\&h-lucidatypewriter-medium-*-*-*-12-*)\n"); fprintf(stderr, "-v Verbose Mode.\n"); fprintf(stderr, "-h Help. This message.\n"); fprintf(stderr, "-m Disable Middle Mouse functionality.\n"); fprintf(stderr, "-s Swap tooltip colors.\n"); fprintf(stderr, "-n Turn off tooltips.\n"); fprintf(stderr, "\n"); exit(0); } /***********************************************************************/ /****** Error Handler Routine *****************************************/ void err_mess(int err, char *str) { switch (err) { case FAILDISP: fprintf(stderr, "Fail: XOpenDisplay for %s\n", str); exit(err); case FAILSWIN: fprintf(stderr, "Fail: XCreateSimpleWindow\n"); exit(err); case FAILICON: fprintf(stderr, "Fail: XCreateSimpleWindow\n"); exit(err); case FAILXPM: fprintf(stderr, "Fail: XCreateBitmapFromData\n"); break; case FAILWNAM: fprintf(stderr, "%s: Can't set up window name\n", str); exit(err); case FAILGC: fprintf(stderr, "Fail: XCreateGC\n"); exit(err); case FAILCONF: fprintf(stderr, "Fail: Can't Find user or system configuration file.\n"); fprintf(stderr, "Fail: User Config: '%s'\n", str); fprintf(stderr, "Fail: System Config: '%s'\n", CONFIGGLOBAL); exit(err); case FAILTMPL: fprintf(stderr, "Fail: Can't Create 'template' Pixmap\n"); exit(err); case FAILVIS: fprintf(stderr, "Fail: Can't Create 'visible' Pixmap\n"); exit(err); case FAILBUT: fprintf(stderr, "Fail: Can't Create 'buttons' Pixmap\n"); exit(err); default: fprintf(stderr, "Fail: UnSpecified Error: %d\n", err); fprintf(stderr, "Fail: %s\n", str); exit(err); } } /***********************************************************************/ /*********************************************************************** * RunAppN(int app) * * Run the command given in the configuration file 'configfile' ***********************************************************************/ void RunAppN(int app) { char *cmndstr; extern struct Config_t Config; cmndstr = Parse(app); /* Get command to pass to system */ if (Config.Verbose) fprintf(stderr, "Command String: %s", cmndstr); if (cmndstr != NULL) { system(cmndstr); /* if there's a command, run it */ free(cmndstr); } } /***********************************************************************/ /*********************************************************************** * Parse(int app) * * Parses the file 'configfile' for command to execute. ***********************************************************************/ char *Parse(int app) { FILE *fp; char Buf[BUFFER_SIZE]; char *Ptr; if ((fp = fopen(Config.configfile, "r")) == NULL) if ((fp = fopen(CONFIGGLOBAL, "r")) == NULL) err_mess(FAILCONF,Config.configfile); while ((Ptr = fgets(Buf, BUFFER_SIZE, fp))) { if (atoi(Buf) == app) break; } fclose(fp); if (!Ptr) return Ptr; Ptr = strchr(Buf, '\t'); /* find first tab */ if (Ptr == NULL) Ptr = strchr(Buf, ' '); /* or space charater */ if (Ptr == NULL) return(NULL); Ptr++; Ptr = strdup(Ptr); return Ptr; } /**********************************************************************/ /*********************************************************************** * initTime * * Copyright (c) 2001 Bruno Essmann ***********************************************************************/ void initTime(void) { extern struct Config_t Config; if (Config.Verbose) fprintf(stdout, "[ ] initializing time\n"); gettimeofday(&_tStart, NULL); } /**********************************************************************/ long currentTimeMillis(void) { struct timeval tNow, tElapsed; gettimeofday(&tNow, NULL); if (_tStart.tv_usec > tNow.tv_usec) { tNow.tv_usec += 1000000; tNow.tv_sec--; } tElapsed.tv_sec = tNow.tv_sec - _tStart.tv_sec; tElapsed.tv_usec = tNow.tv_usec - _tStart.tv_usec; return (tElapsed.tv_sec * 1000) + (tElapsed.tv_usec / 1000); } /**********************************************************************/ void getWindowOrigin(Window w, int *nX, int *nY) { extern Display *display; Window wWindow, wParent, wRoot; Window* wChildren; unsigned int nChildren, ww, wh, wb, wd; int wx, wy; wParent = w; do { wWindow = wParent; if (!XQueryTree(display, wParent, &wRoot, &wParent, &wChildren, &nChildren)) return; if (wChildren) XFree(wChildren); } while (wParent != wRoot); if (XGetGeometry(display, wWindow, &wRoot, &wx, &wy, &ww, &wh, &wb, &wd)) { if (nX) *nX = wx; if (nY) *nY = wy; } } /**********************************************************************/ /*********************************************************************** * getButtonLocation * * compute location for each button's tooltip (not perfect) ***********************************************************************/ void getButtonLocation (int nButton, int *nLocationX, int *nLocationY) { *nLocationX = 0; *nLocationY = 8; while (nButton > BUTTON_COLS) { *nLocationY += BUTTON_SIZE; nButton -= BUTTON_COLS; } while (nButton > 0) { *nLocationX += BUTTON_SIZE - 1; nButton--; } } /**********************************************************************/ /* SkipWord & SkipSpaces: utility functions for getNicenedString */ char *SkipWord(char *Text) { char *Result = Text; while ((*Result != ' ') && (*Result != '\t') && (*Result != '\n') && (*Result != 0x00)) Result++; return Result; } char *SkipSpaces(char *Text) { char *Result = Text; while ((*Result == ' ') || (*Result == '\t') || (*Result == '\n')) Result++; return Result; } /*********************************************************************** * getNicenedString * * nicen the parsed command from the .wmbutton config file * - cut if too long * - remove parameters, whitespace and the '&'... ***********************************************************************/ char *getNicenedString(char *old, int andAddSeparator) { char *WorkStr, *WorkStrEnd, *StartPtr, *EndPtr, *RetStr; if (!old) { if (andAddSeparator) return strdup("-- | "); else return strdup("--"); } RetStr = malloc(strlen(old) + 3 + 1); /* 3 for Seperator */ *RetStr = 0x00; WorkStr = strdup(old); WorkStrEnd = strchr(WorkStr, 0x00); StartPtr = WorkStr; while (StartPtr < WorkStrEnd) { StartPtr = SkipSpaces(StartPtr); EndPtr = SkipWord(StartPtr); *EndPtr = 0x00; if ((*StartPtr == '&') || (*StartPtr == '-')) break; strcat(RetStr, StartPtr); strcat(RetStr, " "); StartPtr = EndPtr + 1; } free(WorkStr); if (andAddSeparator) strcat(RetStr, "| "); return RetStr; } /*********************************************************************** * getButtonAppNames * *returns the 1..3 application names / commands to be shown in tooltip ***********************************************************************/ char *getButtonAppNames(int nButton) { char *tmp1, *tmp2, *str = NULL; if (!( nButton < 0 || nButton > 9 )) { /* FIXME: _Might_ overflow, but it's unlikely. * Perhaps one should fix this sometime ;) */ str = (char*) calloc (sizeof(char), BUFFER_SIZE); tmp1 = Parse(nButton + LMASK); tmp2 = getNicenedString(tmp1, 1); strcat(str, tmp2); free(tmp1); free(tmp2); tmp1 = Parse(nButton + MMASK); tmp2 = getNicenedString(tmp1, 1); strcat(str, tmp2); free(tmp1); free(tmp2); tmp1 = Parse(nButton + RMASK); tmp2 = getNicenedString(tmp1, 0); strcat(str, tmp2); free(tmp1); free(tmp2); } return str; } /**********************************************************************/ int hasTooltipSupport(void) { return !Config.bTooltipDisable; } /**********************************************************************/ void showTooltip (int nButton, int nMouseX, int nMouseY) { Pixmap pixmap, mask; int nMainWinX, nMainWinY; int nButtonX = 0, nButtonY = 0, nButtonWidth = 0, nButtonHeight = 0; int nTextY, nX, nY, nWidth, nHeight, nSide; char* szText; extern struct Config_t Config; extern Window iconwin; extern Pixel bg_pixel, fg_pixel; extern Display *display; extern GC gc; if (Config.bTooltipDisable || nButton == -1) return; if (_bTooltip) hideTooltip(); if (Config.Verbose) fprintf(stdout, "[%8ld] showing tooltip for button %d at %d, %d\n", currentTimeMillis(), nButton, nMouseX, nMouseY); szText = getButtonAppNames(nButton); if(!szText) return; _bTooltip = 1; nWidth = XTextWidth(_fTooltip, szText, strlen(szText)) + 16; nHeight = _nFontHeight + 4; if (nHeight < 16) nHeight = 16; if (nWidth < nHeight) nWidth = nHeight; if (Config.Verbose) fprintf(stdout, "[%8ld] tooltip size: %d, %d\n", currentTimeMillis(), nWidth, nHeight); getWindowOrigin(iconwin, &nMainWinX, &nMainWinY); getButtonLocation(nButton, &nButtonX, &nButtonY); nButtonX += nMainWinX; nButtonY += nMainWinY; nButtonWidth = BUTTON_SIZE; nButtonHeight = BUTTON_SIZE; if (nButtonX + nWidth > _nScreenWidth) { nSide = TOOLTIP_RIGHT; nX = nButtonX - nWidth + nButtonWidth / 2; if (nX < 0) nX = 0; } else { nSide = TOOLTIP_LEFT; nX = nButtonX + nButtonWidth / 2; } if (nX + nWidth > _nScreenWidth) nX = _nScreenWidth - nWidth; if (nButtonY - (nHeight + TOOLTIP_SPACE) < 0) { nSide |= TOOLTIP_TOP; nY = nButtonY + nButtonHeight - 1; nTextY = TOOLTIP_SPACE; } else { nSide |= TOOLTIP_BOTTOM; nY = nButtonY - (nHeight + TOOLTIP_SPACE); nTextY = 0; } pixmap = createTooltipPixmap(nWidth, nHeight, nSide, &mask); XSetForeground(display, gc, Config.bTooltipSwapColors ? fg_pixel : bg_pixel); XSetFont(display, gc, _fTooltip->fid); XDrawString(display, pixmap, gc, 8, nTextY + (nHeight - _nFontHeight) / 2 + _nFontY, szText, strlen(szText)); XSetWindowBackgroundPixmap(display, _wTooltip, pixmap); XResizeWindow(display, _wTooltip, nWidth, nHeight + TOOLTIP_SPACE); XShapeCombineMask(display, _wTooltip, ShapeBounding, 0, 0, mask, ShapeSet); XFreePixmap(display, mask); XMoveWindow(display, _wTooltip, nX, nY); XMapRaised(display, _wTooltip); XFreePixmap(display, pixmap); free(szText); } /**********************************************************************/ void hideTooltip(void) { extern struct Config_t Config; extern Display *display; if (Config.bTooltipDisable) return; if (_bTooltip) { if (Config.Verbose) fprintf(stdout, "[%8ld] hiding tooltip\n", currentTimeMillis()); XUnmapWindow(display, _wTooltip); _bTooltip = 0; } } /**********************************************************************/ int hasTooltip(void) { if (Config.bTooltipDisable) return 0; return _bTooltip; } /**********************************************************************/ void initTooltip(void) { XSetWindowAttributes attribs; unsigned long vmask; extern Display *display; extern char *app_name; extern int screen; extern Window rootwin, win; if (Config.bTooltipDisable) { if (Config.Verbose) fprintf(stdout, "[%8ld] initializing tooltips (disabled)\n", currentTimeMillis()); return; } if (Config.Verbose) fprintf(stdout, "[%8ld] initializing tooltips\n", currentTimeMillis()); _fTooltip = XLoadQueryFont(display, Config.szTooltipFont); if (!_fTooltip) { fprintf(stderr, "%s: couldn't allocate font '%s'.\n", app_name, Config.szTooltipFont); if (!strcmp(Config.szTooltipFont, TOOLTIP_FONT)) fprintf(stderr, "%s: Use option -F \n", app_name); exit(-1); } _nFontHeight = _fTooltip->ascent + _fTooltip->descent; _nFontY = _fTooltip->ascent; _nScreenWidth = WidthOfScreen(ScreenOfDisplay(display, screen)); _nScreenHeight = HeightOfScreen(ScreenOfDisplay(display, screen)); if (Config.Verbose) fprintf(stdout, "[%8ld] configuring tooltip font:\n" \ "[%8ld] - '%s'\n" \ "[%8ld] - font-height= %d, font-ascent= %d\n" \ "[%8ld] configuring screen size: %dx%d\n", currentTimeMillis(), currentTimeMillis(), Config.szTooltipFont, currentTimeMillis(), _nFontHeight, _nFontY, currentTimeMillis(), _nScreenWidth, _nScreenHeight); vmask = CWSaveUnder | CWOverrideRedirect | CWBorderPixel; attribs.save_under = True; attribs.override_redirect = True; attribs.border_pixel = 0; _wTooltip = XCreateWindow(display, rootwin, 1, 1, 10, 10, 1, CopyFromParent, CopyFromParent, CopyFromParent, vmask, &attribs); if (win == 0) { fprintf(stderr, "Cannot create tooltip window.\n"); exit(-1); } } /**********************************************************************/ void destroyTooltip(void) { extern Display *display; if (Config.bTooltipDisable) return; if (_gcMono) { XFreeGC(display, _gcMono); _gcMono = 0; } XDestroyWindow(display, _wTooltip); } /**********************************************************************/ void drawTooltipBalloon(Pixmap pix, GC gc, int x, int y, int w, int h, int side) { extern Display *display; int rad = h * 3 / 10; XPoint pt[3]; XFillArc(display, pix, gc, x, y, rad, rad, 90 * 64, 90 * 64); XFillArc(display, pix, gc, x, y + h - 1 - rad, rad, rad, 180 * 64, 90 * 64); XFillArc(display, pix, gc, x + w - 1 - rad, y, rad, rad, 0 * 64, 90 * 64); XFillArc(display, pix, gc, x + w - 1 - rad, y + h - 1 - rad, rad, rad, 270 * 64, 90 * 64); XFillRectangle(display, pix, gc, x, y + rad / 2, w, h - rad); XFillRectangle(display, pix, gc, x + rad / 2, y, w - rad, h); if (side & TOOLTIP_BOTTOM) { pt[0].y = y + h - 1; pt[1].y = y + h - 1 + TOOLTIP_SPACE; pt[2].y = y + h - 1; } else { pt[0].y = y; pt[1].y = y-TOOLTIP_SPACE; pt[2].y = y; } if (side & TOOLTIP_RIGHT) { pt[0].x = x + w - h + 2 * h / 16; pt[1].x = x + w - h + 11 * h / 16; pt[2].x = x + w - h + 7 * h / 16; } else { pt[0].x = x + h - 2 * h /16; pt[1].x = x + h - 11 * h /16; pt[2].x = x + h - 7 * h /16; } XFillPolygon(display, pix, gc, pt, 3, Convex, CoordModeOrigin); } /**********************************************************************/ Pixmap createTooltipPixmap(int width, int height, int side, Pixmap *mask) { extern Display *display; extern GC gc; extern Pixel bg_pixel, fg_pixel; extern int depth; extern Window rootwin; Pixmap bitmap, pixmap; int x, y; bitmap = XCreatePixmap(display, rootwin, width+TOOLTIP_SPACE, height+TOOLTIP_SPACE, 1); if (!_gcMono) _gcMono = XCreateGC(display, bitmap, 0, NULL); XSetForeground(display, _gcMono, 0); XFillRectangle(display, bitmap, _gcMono, 0, 0, width+TOOLTIP_SPACE, height+TOOLTIP_SPACE); pixmap = XCreatePixmap(display, rootwin, width+TOOLTIP_SPACE, height+TOOLTIP_SPACE, depth); XSetForeground(display, gc, Config.bTooltipSwapColors ? fg_pixel : bg_pixel); XFillRectangle(display, pixmap, gc, 0, 0, width+TOOLTIP_SPACE, height+TOOLTIP_SPACE); if (side & TOOLTIP_BOTTOM) y = 0; else y = TOOLTIP_SPACE; x = 0; XSetForeground(display, _gcMono, 1); drawTooltipBalloon(bitmap, _gcMono, x, y, width, height, side); XSetForeground(display, gc, Config.bTooltipSwapColors ? bg_pixel : fg_pixel); drawTooltipBalloon(pixmap, gc, x+1, y+1, width-2, height-2, side); *mask = bitmap; return pixmap; } /***********************************************************************/ /*********************************************************************** * flush_expose * * Everyone else has one of these... Can't hurt to throw it in. ***********************************************************************/ int flush_expose(Window w) { extern Display *display; XEvent dummy; int i = 0; while (XCheckTypedWindowEvent(display, w, Expose, &dummy)) i++; return i; } /***********************************************************************/