/* 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() { 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 () { extern struct Config_t Config; if (Config.Verbose) { fprintf(stdout, "[ ] initializing time\n"); } gettimeofday(&_tStart, NULL); }/**********************************************************************/ long currentTimeMillis () { struct timeval tNow; struct timeval 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; unsigned int 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; char *WorkStrEnd; char *StartPtr; char *EndPtr; char *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 *str = NULL; char *tmp1,*tmp2; 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 () { 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 () { 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 () { if (Config.bTooltipDisable) { return 0; } return _bTooltip; }/**********************************************************************/ void initTooltip () { 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 () { 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 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); }/***********************************************************************/