/* $Id: wmpager.c,v 1.4 2002/08/16 17:22:26 essmann Exp $ * * Copyright (c) 2001 Bruno Essmann * All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "buttons.xpm" #include "screen.xpm" /* * Constants */ #define AUTHOR "Bruno Essmann " #define APPLICATION "wmpager" #define VERSION "1.2" #define BUILD_REV "$Revision: 1.4 $" #define BUILD_DATE "$Date: 2002/08/16 17:22:26 $" #define XA_WIN_WORKSPACE "_WIN_WORKSPACE" #define XA_WIN_WORKSPACE_NAMES "_WIN_WORKSPACE_NAMES" #define WMPAGER_ENV "WMPAGER" #define WMPAGER_DEFAULT_INSTALL_DIR "/usr/local/share/wmpager/" #define WMPAGER_USER_DIR ".wmpager/" #define TOOLTIP_SUPPORT 1 #define TOOLTIP_FONT "-*-helvetica-bold-r-normal-*-12-*-*-*-*-*-*-*" #define TOOLTIP_OUTSIDE 0 #define TOOLTIP_SHOW_DELAY 750 #define TOOLTIP_RESHOW_DELAY 1500 #define TOOLTIP_SPACE 12 #define TOOLTIP_TOP 0 #define TOOLTIP_BOTTOM 1 #define TOOLTIP_LEFT 0 #define TOOLTIP_RIGHT 2 /* * Prototypes */ void usage (int bVerbose); void info (); void setVerbose (int bVerbose); int isVerbose (); void initApplicationName (char* szApplicationName); char* getApplicationName (); void initDisplay (char* szDisplay); void destroyDisplay (); Display* getDisplay (); Pixel getWhitePixel (); Pixel getBlackPixel (); int getDefaultScreen (); int getDefaultDepth (); void initWindow (int nArgc, char** szArgv); void destroyWindow (); GC getWindowGraphics (); GC getMainGraphics (); void initWindowMask (char* szInstallDir, char* szButtonTheme); void redrawWindow (); void getWindowOrigin (Window w, int* nX, int* nY); void loop (long nTooltipShowDelay, long nTooltipReshowDelay); void initButtons (int nButtons, int nColumns, int nRows); int getButtonCount (); int getButtonRowCount (); int getButtonColumnCount (); int getButtonWidth (); int getButtonHeight (); int getButtonAt (int nLocationX, int nLocationY); void getButtonLocation (int nButton, int* nLocationX, int* nLocationY); void initTime (); long currentTimeMillis (); void initScreens (); int getScreenCount (); char* getScreenName (int nScreen); int getCurrentScreen (); void setCurrentScreen (int nCurrentScreen); void gotoScreen (int nWorkspace); void initTooltip (int bTooltipSupport, char* szFontName, int bTooltipOutside); void destroyTooltip (); int hasTooltipSupport (); void showTooltip (int nButton, int nMouseX, int nMouseY); void hideTooltip (); int hasTooltip (); void drawTooltipBalloon (Pixmap pix, GC gc, int x, int y, int w, int h, int side); Pixmap createTooltipPixmap (int width, int height, int side, Pixmap *mask); /* * Main */ int main (int nArgc, char** szArgv) { char* szDisplay= NULL; char* szTheme= NULL; char* szInstallDir= NULL; char* szTooltipFont= TOOLTIP_FONT; long nTooltipShowDelay= TOOLTIP_SHOW_DELAY; long nTooltipReshowDelay= TOOLTIP_RESHOW_DELAY; int nWorkspaces= -1; int bVerbose= 0; int nSizeX= -1, nSizeY= -1; int bTooltipSupport= TOOLTIP_SUPPORT; int bTooltipOutside= TOOLTIP_OUTSIDE; int i; initApplicationName(szArgv[0]); /* we no longer use the WMPAGER environment variable * szInstallDir= (char*) getenv(WMPAGER_ENV); * instead we simply use a default installation directory */ szInstallDir= WMPAGER_DEFAULT_INSTALL_DIR; i= 1; while (i < nArgc) { if (strcmp("-h", szArgv[i]) == 0 || strcmp("--help", szArgv[i]) == 0) { usage(1); } else if (strcmp("-v", szArgv[i]) == 0 || strcmp("--verbose", szArgv[i]) == 0) { bVerbose= 1; } else if (strcmp("-w", szArgv[i]) == 0 || strcmp("--workspaces", szArgv[i]) == 0) { i+= 1; if (i < nArgc) { sscanf(szArgv[i], "%d", &nWorkspaces); if (nWorkspaces <= 0 || nWorkspaces > 9) { fprintf(stderr, "%s: illegal number of workspaces '%s' for option '%s' (has to be 1-9)\n\n", getApplicationName(), szArgv[i], szArgv[i-1]); usage(0); } } else { fprintf(stderr, "%s: workspace count expected for '%s'\n\n", getApplicationName(), szArgv[i-1]); usage(0); } } else if (strcmp("-s", szArgv[i]) == 0 || strcmp("--size", szArgv[i]) == 0) { i+= 1; if (i < nArgc) { sscanf(szArgv[i], "%dx%d", &nSizeX, &nSizeY); if (nSizeX <= 0 || nSizeX > 3 || nSizeY <= 0 || nSizeY > 3) { fprintf(stderr, "%s: illegal size '%s' for option '%s' (has to be 1x1 .. 3x3)\n\n", getApplicationName(), szArgv[i], szArgv[i-1]); usage(0); } } else { fprintf(stderr, "%s: size argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]); usage(0); } } else if (strcmp("-i", szArgv[i]) == 0 || strcmp("--installdir", szArgv[i]) == 0) { i+= 1; if (i < nArgc) { struct stat buf; szInstallDir= szArgv[i]; if (stat(szInstallDir, &buf) != 0) { fprintf(stderr, "%s: cannot access installation directory '%s'\n\n", getApplicationName(), szArgv[i]); usage(0); } } else { fprintf(stderr, "%s: display argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]); usage(0); } } else if (strcmp("--disable-tooltips", szArgv[i]) == 0) { bTooltipSupport= 0; } else if (strcmp("--tooltip-outside", szArgv[i]) == 0) { bTooltipOutside= 1; } else if (strcmp("--tooltip-font", szArgv[i]) == 0) { i+= 1; if (i < nArgc) { szTooltipFont= szArgv[i]; } else { fprintf(stderr, "%s: font argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]); usage(0); } } else if (strcmp("--tooltip-delay", szArgv[i]) == 0) { i+= 1; if (i < nArgc) { long nDelay= -1; sscanf(szArgv[i], "%ld", &nDelay); if (nDelay < 0) { fprintf(stderr, "%s: illegal tooltip delay '%s' for option '%s'\n\n", getApplicationName(), szArgv[i], szArgv[i-1]); usage(0); } nTooltipShowDelay= nDelay; } else { fprintf(stderr, "%s: delay argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]); usage(0); } } else if (strcmp("--tooltip-reshow", szArgv[i]) == 0) { i+= 1; if (i < nArgc) { long nDelay= -1; sscanf(szArgv[i], "%ld", &nDelay); if (nDelay < 0) { fprintf(stderr, "%s: illegal tooltip delay '%s' for option '%s'\n\n", getApplicationName(), szArgv[i], szArgv[i-1]); usage(0); } nTooltipReshowDelay= nDelay; } else { fprintf(stderr, "%s: delay argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]); usage(0); } } else if (strcmp("-d", szArgv[i]) == 0 || strcmp("--display", szArgv[i]) == 0) { i+= 1; if (i < nArgc) { szDisplay= szArgv[i]; } else { fprintf(stderr, "%s: display argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]); usage(0); } } else if (strcmp("-t", szArgv[i]) == 0 || strcmp("--theme", szArgv[i]) == 0) { i+= 1; if (i < nArgc) { szTheme= szArgv[i]; } else { fprintf(stderr, "%s: theme argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]); usage(0); } } else { fprintf(stderr, "%s: unknown option '%s'\n\n", getApplicationName(), szArgv[i]); usage(0); } i+= 1; } setVerbose(bVerbose); if (isVerbose()) { char* szRealDisplay= (szDisplay == NULL) ? (char*) getenv("DISPLAY") : szDisplay; if (szRealDisplay == NULL) { szRealDisplay= "localhost:0.0"; } info(); fprintf( stdout, "[ ] startup options:\n" \ "[ ] - verbose= true\n" \ "[ ] - display= '%s'\n" \ "[ ] - installdir= '%s'\n" \ "[ ] - theme= '%s'\n" \ "[ ] - workspaces= '%d'\n" \ "[ ] - size= '%dx%d'\n" \ "[ ] - tooltip support= %s\n" \ "[ ] - tooltip font= %s\n" \ "[ ] - tooltip show delay= %ld\n" \ "[ ] - tooltip reshow delay= %ld\n" \ "[ ] - tooltip outside= %s\n", szRealDisplay, szInstallDir == NULL ? "" : szInstallDir, szTheme == NULL ? "" : szTheme, nWorkspaces, nSizeX, nSizeY, bTooltipSupport ? "true" : "false", szTooltipFont, nTooltipShowDelay, nTooltipReshowDelay, bTooltipOutside ? "true" : "false" ); } initTime(); initDisplay(szDisplay); initWindow(nArgc, szArgv); initScreens(); initButtons(nWorkspaces, nSizeX, nSizeY); initWindowMask(szInstallDir, szTheme); initTooltip(bTooltipSupport, szTooltipFont, bTooltipOutside); loop(nTooltipShowDelay, nTooltipReshowDelay); return 0; } /* * Verbose */ static int _bVerbose; void setVerbose (int bVerbose) { _bVerbose= bVerbose; } int isVerbose () { return _bVerbose; } /* * Usage */ #define USAGE \ "usage: %s [options]\n\n" \ "where options include:\n" \ " -h --help display usage and version information\n" \ " -v --verbose verbose message output\n" \ " -d --display the display to use (defaults to the\n" \ " 'DISPLAY' environment variable)\n" \ " -s --size x number of buttons (default depends on the\n" \ " number of workspaces you have, i.e. 2x2 for 4\n" \ " workspaces, 2x3 for 6, maximum is 3x3)\n" \ " -w --workspaces number of workspace buttons to display\n" \ " (default is the number of workspaces you have,\n" \ " maximum is 9)\n" \ " -t --theme the button theme to use, extension\n" \ " '.xpm' is optional, for more information about\n" \ " themes see docu (default is the built-in theme)\n" \ " -i --installdir specifies the installation directory location,\n" \ " this location is automatically searched for themes\n" \ " (defaults to the '/usr/local/share/wmpager/'\n" \ " and the user specific '~/.wmpager' directory)\n" \ " --disable-tooltips do not display any tooltip windows\n" \ " --tooltip-font use the specified font as tooltip font\n" \ " (default is helvetica, bold, roman, 12 point)\n" \ " --tooltip-delay set the delay before the tooltip window\n" \ " is popped up (default is 750 milliseconds)\n" \ " --tooltip-reshow set the tooltip reshow delay (triggered\n" \ " when moving from button to button (default is\n" \ " 1500 milliseconds)\n" \ " --tooltip-outside display tooltip window outside of docklet\n" void usage (int bVerbose) { if (bVerbose) { info(); fprintf(stdout, USAGE, getApplicationName()); exit(0); } else { fprintf(stderr, USAGE, getApplicationName()); exit(-1); } } void info () { char* szRev= strdup(BUILD_REV); char* szDate= strdup(BUILD_DATE); szRev= &szRev[11]; szRev[strlen(szRev) - 2]= '\0'; szDate= &szDate[7]; szDate[strlen(szDate) - 2]= '\0'; fprintf(stdout, "%s %s (build %s, %s)\n\n", APPLICATION, VERSION, szRev, szDate); } /* * Application */ static char* _szApplicationName; char* getApplicationName () { return _szApplicationName; } void initApplicationName (char* szApplicationName) { if (szApplicationName == NULL) { _szApplicationName= APPLICATION; } else { _szApplicationName= strdup(szApplicationName); } } /* * Display */ static Display* _display; Display* getDisplay () { return _display; } void destroyDisplay () { XCloseDisplay(getDisplay()); } void initDisplay (char* szDisplay) { if (szDisplay == NULL && ((char*) getenv("DISPLAY")) == NULL) { szDisplay= ":0.0"; } if (isVerbose()) { char* szRealDisplay= (szDisplay == NULL) ? (char*) getenv("DISPLAY") : szDisplay; if (szRealDisplay == NULL) { szRealDisplay= "localhost:0.0"; } fprintf(stdout, "[%8ld] initializing display '%s'\n", currentTimeMillis(), szRealDisplay); } _display= XOpenDisplay(szDisplay); if (_display == NULL) { fprintf( stderr, "%s: couldn't open display '%s'.\n", getApplicationName(), (szDisplay == NULL) ? ((char*) getenv("DISPLAY")) : szDisplay ); exit(-1); } } /* * Window */ static int _nDefaultScreen, _nDefaultDepth; static Window _wRoot, _wMain, _wIcon; static GC _gcMain, _gcWindow; static XpmAttributes _attrButtonTheme; static Pixmap _pButtonTheme, _pButtonThemeMask; static XpmAttributes _attrWindow; static Pixmap _pWindow, _pWindowMask; static Pixel _pWhite, _pBlack; Pixel getWhitePixel () { return _pWhite; } Pixel getBlackPixel () { return _pBlack; } int getDefaultScreen () { return _nDefaultScreen; } int getDefaultDepth () { return _nDefaultDepth; } Window getRootWindow () { return _wRoot; } Window getMainWindow () { return _wMain; } Window getIconWindow () { return _wIcon; } GC getMainGraphics () { return _gcMain; } GC getWindowGraphics () { return _gcWindow; } void initWindow (int nArgc, char** szArgv) { char* szApplicationName= getApplicationName(); Display* display= getDisplay(); XSizeHints *xsizehints; XWMHints* xwmhints; XClassHint* xclasshint; XTextProperty* xtApplication; XGCValues xgcMain; if (isVerbose()) { fprintf(stdout, "[%8ld] initializing application window\n", currentTimeMillis()); } _nDefaultScreen= DefaultScreen(display); _nDefaultDepth= DefaultDepth(display, _nDefaultScreen); _wRoot= RootWindow(display, _nDefaultScreen); XSelectInput(display, _wRoot, PropertyChangeMask); _pWhite= WhitePixel(display, _nDefaultScreen); _pBlack= BlackPixel(display, _nDefaultScreen); xsizehints= XAllocSizeHints(); xsizehints->flags= USSize | USPosition; xsizehints->width= xsizehints->height= 64; _wMain= XCreateSimpleWindow(display, _wRoot, 0, 0, 64, 64, 5, _pWhite, _pBlack); if (_wMain == 0) { fprintf(stderr, "Cannot create main window.\n"); exit(-1); } _wIcon= XCreateSimpleWindow(display, _wMain, 0, 0, 64, 64, 5, _pWhite, _pBlack); if (_wIcon == 0) { fprintf(stderr, "Cannot create icon window.\n"); exit(-1); } xwmhints= XAllocWMHints(); xwmhints->flags= WindowGroupHint | IconWindowHint | StateHint; xwmhints->icon_window= _wIcon; xwmhints->window_group= _wMain; xwmhints->initial_state= WithdrawnState; XSetWMHints(display, _wMain, xwmhints); xclasshint= XAllocClassHint(); xclasshint->res_name= APPLICATION; xclasshint->res_class= APPLICATION; XSetClassHint(display, _wMain, xclasshint); XSetWMNormalHints(display, _wMain, xsizehints); xtApplication= (XTextProperty*) malloc(sizeof(XTextProperty)); if (XStringListToTextProperty(&szApplicationName, 1, xtApplication) == 0) { fprintf(stderr, "Cannot set window title.\n"); exit(-1); } XSetWMName(display, _wMain, xtApplication); _gcMain= XCreateGC(display, _wMain, (GCForeground | GCBackground), &xgcMain); if (_gcMain == NULL) { fprintf(stderr, "Cannot create graphics context.\n"); exit(-1); } XSelectInput(display, _wMain, ExposureMask | ButtonPressMask | PointerMotionMask | StructureNotifyMask | LeaveWindowMask); XSelectInput(display, _wIcon, ExposureMask | ButtonPressMask | PointerMotionMask | StructureNotifyMask | LeaveWindowMask); XSetCommand(display, _wMain, szArgv, nArgc); XMapWindow(display, _wMain); } void destroyWindow () { XFreeGC(getDisplay(), getWindowGraphics()); XFreeGC(getDisplay(), getMainGraphics()); XDestroyWindow(getDisplay(), getMainWindow()); XDestroyWindow(getDisplay(), getIconWindow()); } void initWindowMask (char* szInstallDir, char* szButtonTheme) { Display* display= getDisplay(); GC gc; Window wRoot= getRootWindow(); Window wMain= getMainWindow(); Window wIcon= getIconWindow(); XGCValues xgc, xgcWindow; Pixmap pOpaque, pTransparent, pMask; char* mask= (char*) malloc(512); int i; if (isVerbose()) { fprintf(stdout, "[%8ld] initializing window mask\n", currentTimeMillis()); } for (i= 0; i < 512; i++) { mask[i]= 0x00; } pTransparent= XCreateBitmapFromData(display, wRoot, mask, 64, 64); if (pTransparent == 0) { fprintf(stderr, "%s: couldn't create window mask (transparent).\n", getApplicationName()); exit(-1); } pMask= XCreateBitmapFromData(display, wRoot, mask, 64, 64); if (pMask == 0) { fprintf(stderr, "%s: couldn't create window mask (mask buffer).\n", getApplicationName()); exit(-1); } for (i= 0; i < 512; i++) { mask[i]= 0xff; } pOpaque= XCreateBitmapFromData(display, wRoot, mask, 64, 64); if (pOpaque == 0) { fprintf(stderr, "%s: couldn't create window mask (opaque).\n", getApplicationName()); exit(-1); } gc= XCreateGC(display, pMask, (GCForeground | GCBackground), &xgc); if (gc == NULL) { fprintf(stderr, "%s: couldn't create window mask (mask graphics).\n", getApplicationName()); exit(-1); } for (i= 0; i < getButtonCount(); i++) { int nButtonX, nButtonY; getButtonLocation(i, &nButtonX, &nButtonY); XCopyArea(display, pOpaque, pMask, gc, nButtonX, nButtonY, getButtonWidth(), getButtonHeight(), nButtonX, nButtonY); } XFreePixmap(display, pOpaque); XFreePixmap(display, pTransparent); XFreeGC(display, gc); XShapeCombineMask(display, wMain, ShapeBounding, 0, 0, pMask, ShapeSet); XShapeCombineMask(display, wIcon, ShapeBounding, 0, 0, pMask, ShapeSet); if (isVerbose()) { fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme == NULL ? "" : szButtonTheme); } _attrButtonTheme.valuemask|= (XpmReturnPixels | XpmReturnExtensions); if (szButtonTheme == NULL) { if ( XpmCreatePixmapFromData( display, wRoot, buttons_xpm, &_pButtonTheme, &_pButtonThemeMask, &_attrButtonTheme ) != XpmSuccess ) { fprintf(stderr, "%s: couldn't create button theme.\n", getApplicationName()); exit(-1); } } else { int bCheckAgain= 0; struct stat buf; /* check for absolute button theme pathname */ if (stat(szButtonTheme, &buf) == -1) { char* szNewTheme= (char*) malloc(strlen(szButtonTheme) + 4); strcpy(szNewTheme, szButtonTheme); strcat(szNewTheme, ".xpm"); if (isVerbose()) { fprintf(stdout, "[%8ld] theme file '%s' not found, trying '%s'\n", currentTimeMillis(), szButtonTheme, szNewTheme); } /* check for absolute button theme pathname (with .xpm added) */ if (stat(szNewTheme, &buf) == 0) { szButtonTheme= szNewTheme; if (isVerbose()) { fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme); } } else { bCheckAgain= 1; free(szNewTheme); } } if (bCheckAgain && szInstallDir != NULL) { char* szNewTheme= (char*) malloc(strlen(szInstallDir) + strlen(szButtonTheme) + 5); strcpy(szNewTheme, szInstallDir); if (szNewTheme[strlen(szNewTheme) - 1] != '/') { strcat(szNewTheme, "/"); } strcat(szNewTheme, szButtonTheme); if (stat(szNewTheme, &buf) == 0) { bCheckAgain= 0; szButtonTheme= szNewTheme; if (isVerbose()) { fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme); } } else { strcat(szNewTheme, ".xpm"); if (stat(szNewTheme, &buf) == 0) { bCheckAgain= 0; szButtonTheme= szNewTheme; if (isVerbose()) { fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme); } } } } if (bCheckAgain) { /* as a goody check the ~/.wmpager directory if it exists */ char* szHome= (char*) getenv("HOME"); if (szHome) { /* one really shouldn't copy&paste but hey this is a q&d tool */ char* szNewTheme= (char*) malloc(strlen(szHome) + strlen(szButtonTheme) + strlen(WMPAGER_USER_DIR) + 5); strcpy(szNewTheme, szHome); if (szNewTheme[strlen(szNewTheme) - 1] != '/') { strcat(szNewTheme, "/"); } strcat(szNewTheme, WMPAGER_USER_DIR); strcat(szNewTheme, szButtonTheme); if (stat(szNewTheme, &buf) == 0) { bCheckAgain= 0; szButtonTheme= szNewTheme; if (isVerbose()) { fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme); } } else { strcat(szNewTheme, ".xpm"); if (stat(szNewTheme, &buf) == 0) { bCheckAgain= 0; szButtonTheme= szNewTheme; if (isVerbose()) { fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme); } } } } } if ( XpmReadFileToPixmap( display, wRoot, szButtonTheme, &_pButtonTheme, &_pButtonThemeMask, &_attrButtonTheme ) != XpmSuccess ) { fprintf(stderr, "%s: couldn't read button theme '%s'.\n", getApplicationName(), szButtonTheme); exit(-1); } } if (isVerbose()) { fprintf(stdout, "[%8ld] initializing screen buffer\n", currentTimeMillis()); } _attrWindow.valuemask|= (XpmReturnPixels | XpmReturnExtensions); if ( XpmCreatePixmapFromData( display, wRoot, screen_xpm, &_pWindow, &_pWindowMask, &_attrWindow ) != XpmSuccess ) { fprintf(stderr, "%s: couldn't create screen buffer.\n", getApplicationName()); exit(-1); } _gcWindow= XCreateGC(_display, _pWindow, (GCForeground | GCBackground), &xgcWindow); if (gc == NULL) { fprintf(stderr, "%s: couldn't create screen buffer graphics.\n", getApplicationName()); exit(-1); } } void redrawWindow () { XEvent event; int i; int w= getButtonWidth(); int h= getButtonHeight(); for (i= 0; i < getButtonCount(); i++) { int x, y, xoff, yoff; xoff= 51; yoff= 10; if (i == getCurrentScreen()) { xoff= 0; yoff= 0; } getButtonLocation(i, &x, &y); XSetClipMask(_display, _gcWindow, _pWindowMask); XSetClipOrigin(_display, _gcWindow, 0, 0); XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff + x - 7, y - 7, w, h, x, y); XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff, 0, w, 1, x, y); XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff, 0, 1, h, x, y); XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff + 50, 0, 1, h, x + w - 1, y); XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff, 50, w, 1, x, y + h - 1); XSetClipMask(_display, _gcWindow, _pButtonThemeMask); XSetClipOrigin(_display, _gcWindow, (x + (w - 10) / 2) - i * 10, -51 - yoff + y + (h - 10) / 2); XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, i * 10, 51 + yoff, 10, 10, x + (w - 10) / 2, y + (h - 10) / 2); } while (XCheckTypedWindowEvent(_display, _wMain, Expose, &event)); XCopyArea(_display, _pWindow, _wMain, _gcMain, 0, 0, 64, 64, 0, 0); while (XCheckTypedWindowEvent(_display, _wIcon, Expose, &event)); XCopyArea(_display, _pWindow, _wIcon, _gcMain, 0, 0, 64, 64, 0, 0); } void getWindowOrigin (Window w, int* nX, int* nY) { 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(getDisplay(), wParent, &wRoot, &wParent, &wChildren, &nChildren)) { return; } if (wChildren) { XFree(wChildren); } } while (wParent != wRoot); if (XGetGeometry(getDisplay(), wWindow, &wRoot, &wx, &wy, &ww, &wh, &wb, &wd)) { if (nX) { *nX= wx; } if (nY) { *nY= wy; } } } /* * Event Loop */ void loop (long nTooltipShowDelay, long nTooltipReshowDelay) { Display* display= getDisplay(); XEvent event; long nTooltipTimer= -1, nTooltipHideTimer= -1, nNow; int nTooltipButton, nTooltipX, nTooltipY; if (isVerbose()) { fprintf(stdout, "[%8ld] starting event loop\n", currentTimeMillis()); } for (;;) { while (XPending(display) || (nTooltipTimer == -1)) { XNextEvent(display, &event); switch (event.type) { case Expose: if (event.xexpose.count == 0) { redrawWindow(); } break; case ConfigureNotify: redrawWindow(); break; case MotionNotify: if (hasTooltipSupport()) { if (!hasTooltip()) { nTooltipTimer= currentTimeMillis(); nTooltipX= event.xbutton.x; nTooltipY= event.xbutton.y; nTooltipButton= getButtonAt(event.xbutton.x, event.xbutton.y); } else { int nButton= getButtonAt(event.xbutton.x, event.xbutton.y); if (nButton != nTooltipButton) { hideTooltip(); nTooltipTimer= -1; nTooltipX= event.xbutton.x; nTooltipY= event.xbutton.y; nTooltipButton= nButton; showTooltip(nTooltipButton, nTooltipX, nTooltipY); } } } break; case LeaveNotify: if (hasTooltip()) { hideTooltip(); nTooltipHideTimer= currentTimeMillis(); } nTooltipTimer= -1; break; case ButtonPress: { int nButton= getButtonAt(event.xbutton.x, event.xbutton.y); if (isVerbose()) { fprintf(stdout, "[%8ld] button %d pressed\n", currentTimeMillis(), nButton); } if (nButton != -1) { if (hasTooltip()) { hideTooltip(); nTooltipHideTimer= currentTimeMillis(); } setCurrentScreen(nButton); gotoScreen(nButton); } } break; case PropertyNotify: if (strcmp(XA_WIN_WORKSPACE, XGetAtomName(getDisplay(), event.xproperty.atom)) == 0) { setCurrentScreen(-1); if (isVerbose()) { fprintf(stdout, "[%8ld] new current workspace (%d= %s)\n", currentTimeMillis(), getCurrentScreen(), getScreenName(getCurrentScreen())); } redrawWindow(); } break; case DestroyNotify: if (isVerbose()) { fprintf(stdout, "[%8ld] quit application\n", currentTimeMillis()); } destroyTooltip(); destroyWindow(); destroyDisplay(); exit(0); break; } } usleep(50000); nNow= currentTimeMillis(); if ( nTooltipTimer != -1 && ( (nNow > nTooltipTimer + nTooltipShowDelay) || (nNow < nTooltipHideTimer + nTooltipReshowDelay) ) ) { showTooltip(nTooltipButton, nTooltipX, nTooltipY); nTooltipTimer= -1; } } } /* * Button Helpers */ static int _nButtons; static int _nButtonRows, _nButtonColumns; static int _nButtonWidth, _nButtonHeight; int getButtonCount () { return _nButtons; } int getButtonRowCount () { return _nButtonRows; } int getButtonColumnCount () { return _nButtonColumns; } int getButtonWidth () { return _nButtonWidth; } int getButtonHeight () { return _nButtonHeight; } int getButtonAt (int nLocationX, int nLocationY) { int i, nButtonX, nButtonY; for (i= 0; i < _nButtons; i++) { getButtonLocation(i, &nButtonX, &nButtonY); if ( nLocationX >= nButtonX && nLocationX < nButtonX + _nButtonWidth && nLocationY >= nButtonY && nLocationY < nButtonY + _nButtonHeight ) { return i; } } return -1; } void getButtonLocation (int nButton, int* nLocationX, int* nLocationY) { if (nButton < 0 || nButton > _nButtons) { *nLocationX= *nLocationY= 0; } else { *nLocationX= *nLocationY= 7; while (nButton >= _nButtonColumns) { *nLocationY+= _nButtonHeight + 3; nButton-= _nButtonColumns; } while (nButton > 0) { *nLocationX+= _nButtonWidth + 3; nButton--; } } } void initButtons (int nButtons, int nColumns, int nRows) { if (nButtons != -1) { _nButtons= nButtons; } else { _nButtons= getScreenCount(); if (_nButtons > 9) { /* only handle nine screens at most */ _nButtons= 9; } } if (nColumns == -1 || nRows == -1) { switch (_nButtons) { case 1: _nButtonRows= _nButtonColumns= 1; break; case 2: _nButtonColumns= 1; _nButtonRows= 2; break; case 3: /* fallthrough */ case 4: _nButtonColumns= _nButtonRows= 2; break; case 5: /* fallthrough */ case 6: _nButtonColumns= 2; _nButtonRows= 3; break; default: _nButtonColumns= _nButtonRows= 3; break; } } else { _nButtonColumns= nColumns; _nButtonRows= nRows; } if (_nButtons > _nButtonColumns * _nButtonRows) { _nButtons= _nButtonColumns * _nButtonRows; } if (isVerbose()) { fprintf(stdout, "[%8ld] initializing buttons\n", currentTimeMillis()); fprintf(stdout, "[%8ld] - %d workspace buttons\n", currentTimeMillis(), _nButtons); fprintf(stdout, "[%8ld] - button layout %dx%d\n", currentTimeMillis(), _nButtonColumns, _nButtonRows); } if (_nButtonColumns == 1) { _nButtonWidth= 51; } else if (_nButtonColumns == 2) { _nButtonWidth= 24; } else { _nButtonWidth= 15; } if (_nButtonRows == 1) { _nButtonHeight= 51; } else if (_nButtonRows == 2) { _nButtonHeight= 24; } else { _nButtonHeight= 15; } } /* * Time */ static struct timeval _tStart; void initTime () { if (isVerbose()) { 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); } /* * Screen Handling */ static Atom _xaWinWorkspace; static Atom _xaWinWorkspaceNames; static int _nScreens; static char** _szScreenNames; static int _nLastScreen; int getScreenCount () { return _nScreens; } char* getScreenName (int nScreen) { if (nScreen < 0 || nScreen >= _nScreens) { return NULL; } return _szScreenNames[nScreen]; } void gotoScreen (int nWorkspace) { XEvent event; event.type= ClientMessage; event.xclient.type= ClientMessage; event.xclient.window= getRootWindow(); event.xclient.message_type= _xaWinWorkspace; event.xclient.format= 32; event.xclient.data.l[0]= nWorkspace; event.xclient.data.l[1]= currentTimeMillis(); XSendEvent(getDisplay(), getRootWindow(), False, SubstructureNotifyMask, (XEvent*) &event); } int getCurrentScreen () { return _nLastScreen; } void setCurrentScreen (int nCurrentScreen) { if (nCurrentScreen == -1) { long nScreen= _nLastScreen; Atom xaType; int nFormat; unsigned long nItems, nBytesAfter; unsigned char* data; XGetWindowProperty( getDisplay(), getRootWindow(), _xaWinWorkspace, 0, 8192, False, XA_CARDINAL, &xaType, &nFormat, &nItems, &nBytesAfter, &data ); if ((nFormat == 32) && (nItems == 1) && (nBytesAfter == 0)) { nScreen= *(long*) data; } if (xaType != None) { XFree(data); } _nLastScreen= nScreen; } else { _nLastScreen= nCurrentScreen; } } void initScreens () { XTextProperty tp; if (isVerbose()) { fprintf(stdout, "[%8ld] initializing window maker communication\n", currentTimeMillis()); } _xaWinWorkspace= XInternAtom(getDisplay(), XA_WIN_WORKSPACE, False); _xaWinWorkspaceNames= XInternAtom(getDisplay(), XA_WIN_WORKSPACE_NAMES, False); XGetTextProperty(getDisplay(), getRootWindow(), &tp, _xaWinWorkspaceNames); XTextPropertyToStringList(&tp, &_szScreenNames, &_nScreens); _nLastScreen= -1; setCurrentScreen(-1); if (_nLastScreen == -1) { fprintf( stderr, "%s: couldn't determine current workspace.\n" \ "Make sure your WindowMaker has Gnome support enabled!\n", getApplicationName() ); setCurrentScreen(0); } if (isVerbose()) { int i; fprintf(stdout, "[%8ld] - %d worspaces found\n", currentTimeMillis(), getScreenCount()); for (i= 0; i < getScreenCount(); i++) { fprintf(stdout, "[%8ld] - workspace %d: %s\n", currentTimeMillis(), i, getScreenName(i)); } fprintf(stdout, "[%8ld] - current workspace is %d (%s)\n", currentTimeMillis(), getCurrentScreen(), getScreenName(getCurrentScreen())); } } /* * Tooltip */ int _bTooltip= 0, _bTooltipSupport, _bTooltipOutside; XFontStruct* _fTooltip; int _nFontHeight, _nFontY; int _nScreenWidth, _nScreenHeight; GC _gcMono= 0; Window _wTooltip; int hasTooltipSupport () { return _bTooltipSupport; } void showTooltip (int nButton, int nMouseX, int nMouseY) { Pixmap pixmap, mask; int nMainWinX, nMainWinY; int nButtonX, nButtonY, nButtonWidth, nButtonHeight; int nTextY, nX, nY, nWidth, nHeight, nSide; char* szText; if (!_bTooltipSupport) { return; } if (_bTooltip) { hideTooltip(); } _bTooltip= 1; if (isVerbose()) { fprintf(stdout, "[%8ld] showing tooltip for button %d (%s) at %d, %d\n", currentTimeMillis(), nButton, getScreenName(nButton), nMouseX, nMouseY); } szText= strdup(getScreenName(nButton)); nWidth= XTextWidth(_fTooltip, szText, strlen(szText)) + 16; nHeight= _nFontHeight + 4; if (nHeight < 16) { nHeight= 16; } if (nWidth < nHeight) { nWidth= nHeight; } if (isVerbose()) { fprintf(stdout, "[%8ld] tooltip size: %d, %d\n", currentTimeMillis(), nWidth, nHeight); } getWindowOrigin(getIconWindow(), &nMainWinX, &nMainWinY); if (_bTooltipOutside) { nButtonX= nMainWinX; nButtonY= nMainWinY; nButtonWidth= 64; nButtonHeight= 64; } else { getButtonLocation(nButton, &nButtonX, &nButtonY); nButtonX+= nMainWinX; nButtonY+= nMainWinY; nButtonWidth= getButtonWidth(); nButtonHeight= getButtonHeight(); } 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(getDisplay(), getMainGraphics(), getBlackPixel()); XSetFont(getDisplay(), getMainGraphics(), _fTooltip->fid); XDrawString(getDisplay(), pixmap, getMainGraphics(), 8, nTextY + (nHeight - _nFontHeight) / 2 + _nFontY, szText, strlen(szText)); XSetWindowBackgroundPixmap(getDisplay(), _wTooltip, pixmap); XResizeWindow(getDisplay(), _wTooltip, nWidth, nHeight + TOOLTIP_SPACE); XShapeCombineMask(getDisplay(), _wTooltip, ShapeBounding, 0, 0, mask, ShapeSet); XFreePixmap(getDisplay(), mask); XMoveWindow(getDisplay(), _wTooltip, nX, nY); XMapRaised(getDisplay(), _wTooltip); XFreePixmap(getDisplay(), pixmap); free(szText); } void hideTooltip () { if (!_bTooltipSupport) { return; } if (_bTooltip) { if (isVerbose()) { fprintf(stdout, "[%8ld] hiding tooltip\n", currentTimeMillis()); } XUnmapWindow(getDisplay(), _wTooltip); _bTooltip= 0; } } int hasTooltip () { if (!_bTooltipSupport) { return 0; } return _bTooltip; } void initTooltip (int bTooltipSupport, char* szFontName, int bTooltipOutside) { XSetWindowAttributes attribs; unsigned long vmask; _bTooltipSupport= bTooltipSupport; if (!_bTooltipSupport) { if (isVerbose()) { fprintf(stdout, "[%8ld] initializing tooltips (disabled)\n", currentTimeMillis()); } return; } if (isVerbose()) { fprintf(stdout, "[%8ld] initializing tooltips\n", currentTimeMillis()); } _bTooltipOutside= bTooltipOutside; _fTooltip= XLoadQueryFont(getDisplay(), szFontName); if (!_fTooltip) { fprintf(stderr, "%s: couldn't allocate font '%s'.\n", getApplicationName(), szFontName); exit(-1); } _nFontHeight= _fTooltip->ascent + _fTooltip->descent; _nFontY= _fTooltip->ascent; _nScreenWidth= WidthOfScreen(ScreenOfDisplay(getDisplay(), getDefaultScreen())); _nScreenHeight= HeightOfScreen(ScreenOfDisplay(getDisplay(), getDefaultScreen())); if (isVerbose()) { 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(), szFontName, currentTimeMillis(), _nFontHeight, _nFontY, currentTimeMillis(), _nScreenWidth, _nScreenHeight ); } vmask= CWSaveUnder | CWOverrideRedirect | CWBorderPixel; attribs.save_under= True; attribs.override_redirect= True; attribs.border_pixel= 0; _wTooltip= XCreateWindow(getDisplay(), getRootWindow(), 1, 1, 10, 10, 1, CopyFromParent, CopyFromParent, CopyFromParent, vmask, &attribs); if (_wMain == 0) { fprintf(stderr, "Cannot create tooltip window.\n"); exit(-1); } } void destroyTooltip () { if (!_bTooltipSupport) { return; } if (_gcMono) { XFreeGC(getDisplay(), _gcMono); _gcMono= 0; } XDestroyWindow(getDisplay(), _wTooltip); } void drawTooltipBalloon (Pixmap pix, GC gc, int x, int y, int w, int h, int side) { Display* display= getDisplay(); 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) { Display* display= getDisplay(); Window wRoot= getRootWindow(); GC gc= getMainGraphics(); Pixmap bitmap; Pixmap pixmap; int x, y; bitmap = XCreatePixmap(display, wRoot, 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, wRoot, width+TOOLTIP_SPACE, height+TOOLTIP_SPACE, getDefaultDepth()); XSetForeground(display, gc, getBlackPixel()); 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, getWhitePixel()); drawTooltipBalloon(pixmap, gc, x+1, y+1, width-2, height-2, side); *mask = bitmap; return pixmap; }