1 /* $Id: wmpager.c,v 1.4 2002/08/16 17:22:26 essmann Exp $
3 * Copyright (c) 2001 Bruno Essmann <essmann@users.sourceforge.net>
11 #include <X11/extensions/shape.h>
13 #include <sys/types.h>
20 #include "buttons.xpm"
27 #define AUTHOR "Bruno Essmann <essmann@users.sourceforge.net>"
28 #define APPLICATION "wmpager"
30 #define BUILD_REV "$Revision: 1.4 $"
31 #define BUILD_DATE "$Date: 2002/08/16 17:22:26 $"
33 #define XA_WIN_WORKSPACE "_WIN_WORKSPACE"
34 #define XA_WIN_WORKSPACE_NAMES "_WIN_WORKSPACE_NAMES"
36 #define WMPAGER_ENV "WMPAGER"
37 #define WMPAGER_DEFAULT_INSTALL_DIR "/usr/local/share/wmpager/"
38 #define WMPAGER_USER_DIR ".wmpager/"
40 #define TOOLTIP_SUPPORT 1
41 #define TOOLTIP_FONT "-*-helvetica-bold-r-normal-*-12-*-*-*-*-*-*-*"
42 #define TOOLTIP_OUTSIDE 0
43 #define TOOLTIP_SHOW_DELAY 750
44 #define TOOLTIP_RESHOW_DELAY 1500
46 #define TOOLTIP_SPACE 12
48 #define TOOLTIP_BOTTOM 1
49 #define TOOLTIP_LEFT 0
50 #define TOOLTIP_RIGHT 2
56 void usage (int bVerbose
);
58 void setVerbose (int bVerbose
);
61 void initApplicationName (char* szApplicationName
);
62 char* getApplicationName ();
64 void initDisplay (char* szDisplay
);
65 void destroyDisplay ();
66 Display
* getDisplay ();
68 Pixel
getWhitePixel ();
69 Pixel
getBlackPixel ();
70 int getDefaultScreen ();
71 int getDefaultDepth ();
72 void initWindow (int nArgc
, char** szArgv
);
73 void destroyWindow ();
74 GC
getWindowGraphics ();
75 GC
getMainGraphics ();
76 void initWindowMask (char* szInstallDir
, char* szButtonTheme
);
78 void getWindowOrigin (Window w
, int* nX
, int* nY
);
80 void loop (long nTooltipShowDelay
, long nTooltipReshowDelay
);
82 void initButtons (int nButtons
, int nColumns
, int nRows
);
83 int getButtonCount ();
84 int getButtonRowCount ();
85 int getButtonColumnCount ();
86 int getButtonWidth ();
87 int getButtonHeight ();
88 int getButtonAt (int nLocationX
, int nLocationY
);
89 void getButtonLocation (int nButton
, int* nLocationX
, int* nLocationY
);
92 long currentTimeMillis ();
95 int getScreenCount ();
96 char* getScreenName (int nScreen
);
97 int getCurrentScreen ();
98 void setCurrentScreen (int nCurrentScreen
);
99 void gotoScreen (int nWorkspace
);
101 void initTooltip (int bTooltipSupport
, char* szFontName
, int bTooltipOutside
);
102 void destroyTooltip ();
103 int hasTooltipSupport ();
104 void showTooltip (int nButton
, int nMouseX
, int nMouseY
);
107 void drawTooltipBalloon (Pixmap pix
, GC gc
, int x
, int y
, int w
, int h
, int side
);
108 Pixmap
createTooltipPixmap (int width
, int height
, int side
, Pixmap
*mask
);
114 int main (int nArgc
, char** szArgv
) {
115 char* szDisplay
= NULL
;
117 char* szInstallDir
= NULL
;
118 char* szTooltipFont
= TOOLTIP_FONT
;
119 long nTooltipShowDelay
= TOOLTIP_SHOW_DELAY
;
120 long nTooltipReshowDelay
= TOOLTIP_RESHOW_DELAY
;
123 int nSizeX
= -1, nSizeY
= -1;
124 int bTooltipSupport
= TOOLTIP_SUPPORT
;
125 int bTooltipOutside
= TOOLTIP_OUTSIDE
;
127 initApplicationName(szArgv
[0]);
128 /* we no longer use the WMPAGER environment variable
129 * szInstallDir= (char*) getenv(WMPAGER_ENV);
130 * instead we simply use a default installation directory
132 szInstallDir
= WMPAGER_DEFAULT_INSTALL_DIR
;
135 if (strcmp("-h", szArgv
[i
]) == 0 || strcmp("--help", szArgv
[i
]) == 0) {
137 } else if (strcmp("-v", szArgv
[i
]) == 0 || strcmp("--verbose", szArgv
[i
]) == 0) {
139 } else if (strcmp("-w", szArgv
[i
]) == 0 || strcmp("--workspaces", szArgv
[i
]) == 0) {
142 sscanf(szArgv
[i
], "%d", &nWorkspaces
);
143 if (nWorkspaces
<= 0 || nWorkspaces
> 9) {
144 fprintf(stderr
, "%s: illegal number of workspaces '%s' for option '%s' (has to be 1-9)\n\n", getApplicationName(), szArgv
[i
], szArgv
[i
-1]);
148 fprintf(stderr
, "%s: workspace count expected for '%s'\n\n", getApplicationName(), szArgv
[i
-1]);
151 } else if (strcmp("-s", szArgv
[i
]) == 0 || strcmp("--size", szArgv
[i
]) == 0) {
154 sscanf(szArgv
[i
], "%dx%d", &nSizeX
, &nSizeY
);
155 if (nSizeX
<= 0 || nSizeX
> 3 || nSizeY
<= 0 || nSizeY
> 3) {
156 fprintf(stderr
, "%s: illegal size '%s' for option '%s' (has to be 1x1 .. 3x3)\n\n", getApplicationName(), szArgv
[i
], szArgv
[i
-1]);
160 fprintf(stderr
, "%s: size argument expected for '%s'\n\n", getApplicationName(), szArgv
[i
-1]);
163 } else if (strcmp("-i", szArgv
[i
]) == 0 || strcmp("--installdir", szArgv
[i
]) == 0) {
167 szInstallDir
= szArgv
[i
];
168 if (stat(szInstallDir
, &buf
) != 0) {
169 fprintf(stderr
, "%s: cannot access installation directory '%s'\n\n", getApplicationName(), szArgv
[i
]);
173 fprintf(stderr
, "%s: display argument expected for '%s'\n\n", getApplicationName(), szArgv
[i
-1]);
176 } else if (strcmp("--disable-tooltips", szArgv
[i
]) == 0) {
178 } else if (strcmp("--tooltip-outside", szArgv
[i
]) == 0) {
180 } else if (strcmp("--tooltip-font", szArgv
[i
]) == 0) {
183 szTooltipFont
= szArgv
[i
];
185 fprintf(stderr
, "%s: font argument expected for '%s'\n\n", getApplicationName(), szArgv
[i
-1]);
188 } else if (strcmp("--tooltip-delay", szArgv
[i
]) == 0) {
192 sscanf(szArgv
[i
], "%ld", &nDelay
);
194 fprintf(stderr
, "%s: illegal tooltip delay '%s' for option '%s'\n\n", getApplicationName(), szArgv
[i
], szArgv
[i
-1]);
197 nTooltipShowDelay
= nDelay
;
199 fprintf(stderr
, "%s: delay argument expected for '%s'\n\n", getApplicationName(), szArgv
[i
-1]);
202 } else if (strcmp("--tooltip-reshow", szArgv
[i
]) == 0) {
206 sscanf(szArgv
[i
], "%ld", &nDelay
);
208 fprintf(stderr
, "%s: illegal tooltip delay '%s' for option '%s'\n\n", getApplicationName(), szArgv
[i
], szArgv
[i
-1]);
211 nTooltipReshowDelay
= nDelay
;
213 fprintf(stderr
, "%s: delay argument expected for '%s'\n\n", getApplicationName(), szArgv
[i
-1]);
216 } else if (strcmp("-d", szArgv
[i
]) == 0 || strcmp("--display", szArgv
[i
]) == 0) {
219 szDisplay
= szArgv
[i
];
221 fprintf(stderr
, "%s: display argument expected for '%s'\n\n", getApplicationName(), szArgv
[i
-1]);
224 } else if (strcmp("-t", szArgv
[i
]) == 0 || strcmp("--theme", szArgv
[i
]) == 0) {
229 fprintf(stderr
, "%s: theme argument expected for '%s'\n\n", getApplicationName(), szArgv
[i
-1]);
233 fprintf(stderr
, "%s: unknown option '%s'\n\n", getApplicationName(), szArgv
[i
]);
238 setVerbose(bVerbose
);
240 char* szRealDisplay
= (szDisplay
== NULL
) ? (char*) getenv("DISPLAY") : szDisplay
;
241 if (szRealDisplay
== NULL
) {
242 szRealDisplay
= "localhost:0.0";
247 "[ ] startup options:\n" \
248 "[ ] - verbose= true\n" \
249 "[ ] - display= '%s'\n" \
250 "[ ] - installdir= '%s'\n" \
251 "[ ] - theme= '%s'\n" \
252 "[ ] - workspaces= '%d'\n" \
253 "[ ] - size= '%dx%d'\n" \
254 "[ ] - tooltip support= %s\n" \
255 "[ ] - tooltip font= %s\n" \
256 "[ ] - tooltip show delay= %ld\n" \
257 "[ ] - tooltip reshow delay= %ld\n" \
258 "[ ] - tooltip outside= %s\n",
260 szInstallDir
== NULL
? "<undefined>" : szInstallDir
,
261 szTheme
== NULL
? "<built-in>" : szTheme
,
264 bTooltipSupport
? "true" : "false",
266 nTooltipShowDelay
, nTooltipReshowDelay
,
267 bTooltipOutside
? "true" : "false"
271 initDisplay(szDisplay
);
272 initWindow(nArgc
, szArgv
);
274 initButtons(nWorkspaces
, nSizeX
, nSizeY
);
275 initWindowMask(szInstallDir
, szTheme
);
276 initTooltip(bTooltipSupport
, szTooltipFont
, bTooltipOutside
);
277 loop(nTooltipShowDelay
, nTooltipReshowDelay
);
285 static int _bVerbose
;
287 void setVerbose (int bVerbose
) {
300 "usage: %s [options]\n\n" \
301 "where options include:\n" \
302 " -h --help display usage and version information\n" \
303 " -v --verbose verbose message output\n" \
304 " -d --display <name> the display to use (defaults to the\n" \
305 " 'DISPLAY' environment variable)\n" \
306 " -s --size <w>x<h> number of buttons (default depends on the\n" \
307 " number of workspaces you have, i.e. 2x2 for 4\n" \
308 " workspaces, 2x3 for 6, maximum is 3x3)\n" \
309 " -w --workspaces <count> number of workspace buttons to display\n" \
310 " (default is the number of workspaces you have,\n" \
312 " -t --theme <theme.xpm> the button theme to use, extension\n" \
313 " '.xpm' is optional, for more information about\n" \
314 " themes see docu (default is the built-in theme)\n" \
315 " -i --installdir <dir> specifies the installation directory location,\n" \
316 " this location is automatically searched for themes\n" \
317 " (defaults to the '/usr/local/share/wmpager/'\n" \
318 " and the user specific '~/.wmpager' directory)\n" \
319 " --disable-tooltips do not display any tooltip windows\n" \
320 " --tooltip-font <font> use the specified font as tooltip font\n" \
321 " (default is helvetica, bold, roman, 12 point)\n" \
322 " --tooltip-delay <millis> set the delay before the tooltip window\n" \
323 " is popped up (default is 750 milliseconds)\n" \
324 " --tooltip-reshow <millis> set the tooltip reshow delay (triggered\n" \
325 " when moving from button to button (default is\n" \
326 " 1500 milliseconds)\n" \
327 " --tooltip-outside display tooltip window outside of docklet\n"
329 void usage (int bVerbose
) {
332 fprintf(stdout
, USAGE
, getApplicationName());
335 fprintf(stderr
, USAGE
, getApplicationName());
341 char* szRev
= strdup(BUILD_REV
);
342 char* szDate
= strdup(BUILD_DATE
);
344 szRev
[strlen(szRev
) - 2]= '\0';
346 szDate
[strlen(szDate
) - 2]= '\0';
347 fprintf(stdout
, "%s %s (build %s, %s)\n\n", APPLICATION
, VERSION
, szRev
, szDate
);
354 static char* _szApplicationName
;
356 char* getApplicationName () {
357 return _szApplicationName
;
360 void initApplicationName (char* szApplicationName
) {
361 if (szApplicationName
== NULL
) {
362 _szApplicationName
= APPLICATION
;
364 _szApplicationName
= strdup(szApplicationName
);
372 static Display
* _display
;
374 Display
* getDisplay () {
378 void destroyDisplay () {
379 XCloseDisplay(getDisplay());
382 void initDisplay (char* szDisplay
) {
383 if (szDisplay
== NULL
&& ((char*) getenv("DISPLAY")) == NULL
) {
387 char* szRealDisplay
= (szDisplay
== NULL
) ? (char*) getenv("DISPLAY") : szDisplay
;
388 if (szRealDisplay
== NULL
) {
389 szRealDisplay
= "localhost:0.0";
391 fprintf(stdout
, "[%8ld] initializing display '%s'\n", currentTimeMillis(), szRealDisplay
);
393 _display
= XOpenDisplay(szDisplay
);
394 if (_display
== NULL
) {
397 "%s: couldn't open display '%s'.\n",
398 getApplicationName(),
399 (szDisplay
== NULL
) ? ((char*) getenv("DISPLAY")) : szDisplay
409 static int _nDefaultScreen
, _nDefaultDepth
;
410 static Window _wRoot
, _wMain
, _wIcon
;
411 static GC _gcMain
, _gcWindow
;
412 static XpmAttributes _attrButtonTheme
;
413 static Pixmap _pButtonTheme
, _pButtonThemeMask
;
414 static XpmAttributes _attrWindow
;
415 static Pixmap _pWindow
, _pWindowMask
;
416 static Pixel _pWhite
, _pBlack
;
418 Pixel
getWhitePixel () {
422 Pixel
getBlackPixel () {
426 int getDefaultScreen () {
427 return _nDefaultScreen
;
430 int getDefaultDepth () {
431 return _nDefaultDepth
;
434 Window
getRootWindow () {
438 Window
getMainWindow () {
442 Window
getIconWindow () {
446 GC
getMainGraphics () {
450 GC
getWindowGraphics () {
454 void initWindow (int nArgc
, char** szArgv
) {
455 char* szApplicationName
= getApplicationName();
456 Display
* display
= getDisplay();
457 XSizeHints
*xsizehints
;
459 XClassHint
* xclasshint
;
460 XTextProperty
* xtApplication
;
464 fprintf(stdout
, "[%8ld] initializing application window\n", currentTimeMillis());
467 _nDefaultScreen
= DefaultScreen(display
);
468 _nDefaultDepth
= DefaultDepth(display
, _nDefaultScreen
);
469 _wRoot
= RootWindow(display
, _nDefaultScreen
);
471 XSelectInput(display
, _wRoot
, PropertyChangeMask
);
473 _pWhite
= WhitePixel(display
, _nDefaultScreen
);
474 _pBlack
= BlackPixel(display
, _nDefaultScreen
);
476 xsizehints
= XAllocSizeHints();
477 xsizehints
->flags
= USSize
| USPosition
;
478 xsizehints
->width
= xsizehints
->height
= 64;
480 _wMain
= XCreateSimpleWindow(display
, _wRoot
, 0, 0, 64, 64, 5, _pWhite
, _pBlack
);
482 fprintf(stderr
, "Cannot create main window.\n");
486 _wIcon
= XCreateSimpleWindow(display
, _wMain
, 0, 0, 64, 64, 5, _pWhite
, _pBlack
);
488 fprintf(stderr
, "Cannot create icon window.\n");
492 xwmhints
= XAllocWMHints();
493 xwmhints
->flags
= WindowGroupHint
| IconWindowHint
| StateHint
;
494 xwmhints
->icon_window
= _wIcon
;
495 xwmhints
->window_group
= _wMain
;
496 xwmhints
->initial_state
= WithdrawnState
;
497 XSetWMHints(display
, _wMain
, xwmhints
);
499 xclasshint
= XAllocClassHint();
500 xclasshint
->res_name
= APPLICATION
;
501 xclasshint
->res_class
= APPLICATION
;
502 XSetClassHint(display
, _wMain
, xclasshint
);
504 XSetWMNormalHints(display
, _wMain
, xsizehints
);
506 xtApplication
= (XTextProperty
*) malloc(sizeof(XTextProperty
));
507 if (XStringListToTextProperty(&szApplicationName
, 1, xtApplication
) == 0) {
508 fprintf(stderr
, "Cannot set window title.\n");
511 XSetWMName(display
, _wMain
, xtApplication
);
513 _gcMain
= XCreateGC(display
, _wMain
, (GCForeground
| GCBackground
), &xgcMain
);
514 if (_gcMain
== NULL
) {
515 fprintf(stderr
, "Cannot create graphics context.\n");
519 XSelectInput(display
, _wMain
, ExposureMask
| ButtonPressMask
| PointerMotionMask
| StructureNotifyMask
| LeaveWindowMask
);
520 XSelectInput(display
, _wIcon
, ExposureMask
| ButtonPressMask
| PointerMotionMask
| StructureNotifyMask
| LeaveWindowMask
);
522 XSetCommand(display
, _wMain
, szArgv
, nArgc
);
524 XMapWindow(display
, _wMain
);
527 void destroyWindow () {
528 XFreeGC(getDisplay(), getWindowGraphics());
529 XFreeGC(getDisplay(), getMainGraphics());
530 XDestroyWindow(getDisplay(), getMainWindow());
531 XDestroyWindow(getDisplay(), getIconWindow());
534 void initWindowMask (char* szInstallDir
, char* szButtonTheme
) {
535 Display
* display
= getDisplay();
537 Window wRoot
= getRootWindow();
538 Window wMain
= getMainWindow();
539 Window wIcon
= getIconWindow();
540 XGCValues xgc
, xgcWindow
;
541 Pixmap pOpaque
, pTransparent
, pMask
;
542 char* mask
= (char*) malloc(512);
546 fprintf(stdout
, "[%8ld] initializing window mask\n", currentTimeMillis());
548 for (i
= 0; i
< 512; i
++) {
551 pTransparent
= XCreateBitmapFromData(display
, wRoot
, mask
, 64, 64);
552 if (pTransparent
== 0) {
553 fprintf(stderr
, "%s: couldn't create window mask (transparent).\n", getApplicationName());
556 pMask
= XCreateBitmapFromData(display
, wRoot
, mask
, 64, 64);
558 fprintf(stderr
, "%s: couldn't create window mask (mask buffer).\n", getApplicationName());
562 for (i
= 0; i
< 512; i
++) {
565 pOpaque
= XCreateBitmapFromData(display
, wRoot
, mask
, 64, 64);
567 fprintf(stderr
, "%s: couldn't create window mask (opaque).\n", getApplicationName());
571 gc
= XCreateGC(display
, pMask
, (GCForeground
| GCBackground
), &xgc
);
573 fprintf(stderr
, "%s: couldn't create window mask (mask graphics).\n", getApplicationName());
576 for (i
= 0; i
< getButtonCount(); i
++) {
577 int nButtonX
, nButtonY
;
578 getButtonLocation(i
, &nButtonX
, &nButtonY
);
579 XCopyArea(display
, pOpaque
, pMask
, gc
, nButtonX
, nButtonY
, getButtonWidth(), getButtonHeight(), nButtonX
, nButtonY
);
582 XFreePixmap(display
, pOpaque
);
583 XFreePixmap(display
, pTransparent
);
584 XFreeGC(display
, gc
);
586 XShapeCombineMask(display
, wMain
, ShapeBounding
, 0, 0, pMask
, ShapeSet
);
587 XShapeCombineMask(display
, wIcon
, ShapeBounding
, 0, 0, pMask
, ShapeSet
);
590 fprintf(stdout
, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(),
591 szButtonTheme
== NULL
? "<built-in>" : szButtonTheme
);
594 _attrButtonTheme
.valuemask
|= (XpmReturnPixels
| XpmReturnExtensions
);
595 if (szButtonTheme
== NULL
) {
597 XpmCreatePixmapFromData(
598 display
, wRoot
, buttons_xpm
, &_pButtonTheme
, &_pButtonThemeMask
, &_attrButtonTheme
601 fprintf(stderr
, "%s: couldn't create button theme.\n", getApplicationName());
607 /* check for absolute button theme pathname */
608 if (stat(szButtonTheme
, &buf
) == -1) {
609 char* szNewTheme
= (char*) malloc(strlen(szButtonTheme
) + 4);
610 strcpy(szNewTheme
, szButtonTheme
);
611 strcat(szNewTheme
, ".xpm");
613 fprintf(stdout
, "[%8ld] theme file '%s' not found, trying '%s'\n", currentTimeMillis(), szButtonTheme
, szNewTheme
);
615 /* check for absolute button theme pathname (with .xpm added) */
616 if (stat(szNewTheme
, &buf
) == 0) {
617 szButtonTheme
= szNewTheme
;
619 fprintf(stdout
, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme
);
626 if (bCheckAgain
&& szInstallDir
!= NULL
) {
627 char* szNewTheme
= (char*) malloc(strlen(szInstallDir
) + strlen(szButtonTheme
) + 5);
628 strcpy(szNewTheme
, szInstallDir
);
629 if (szNewTheme
[strlen(szNewTheme
) - 1] != '/') {
630 strcat(szNewTheme
, "/");
632 strcat(szNewTheme
, szButtonTheme
);
633 if (stat(szNewTheme
, &buf
) == 0) {
635 szButtonTheme
= szNewTheme
;
637 fprintf(stdout
, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme
);
640 strcat(szNewTheme
, ".xpm");
641 if (stat(szNewTheme
, &buf
) == 0) {
643 szButtonTheme
= szNewTheme
;
645 fprintf(stdout
, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme
);
651 /* as a goody check the ~/.wmpager directory if it exists */
652 char* szHome
= (char*) getenv("HOME");
654 /* one really shouldn't copy&paste but hey this is a q&d tool */
655 char* szNewTheme
= (char*) malloc(strlen(szHome
) + strlen(szButtonTheme
) + strlen(WMPAGER_USER_DIR
) + 5);
656 strcpy(szNewTheme
, szHome
);
657 if (szNewTheme
[strlen(szNewTheme
) - 1] != '/') {
658 strcat(szNewTheme
, "/");
660 strcat(szNewTheme
, WMPAGER_USER_DIR
);
661 strcat(szNewTheme
, szButtonTheme
);
662 if (stat(szNewTheme
, &buf
) == 0) {
664 szButtonTheme
= szNewTheme
;
666 fprintf(stdout
, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme
);
669 strcat(szNewTheme
, ".xpm");
670 if (stat(szNewTheme
, &buf
) == 0) {
672 szButtonTheme
= szNewTheme
;
674 fprintf(stdout
, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme
);
682 display
, wRoot
, szButtonTheme
, &_pButtonTheme
, &_pButtonThemeMask
, &_attrButtonTheme
685 fprintf(stderr
, "%s: couldn't read button theme '%s'.\n", getApplicationName(), szButtonTheme
);
691 fprintf(stdout
, "[%8ld] initializing screen buffer\n", currentTimeMillis());
694 _attrWindow
.valuemask
|= (XpmReturnPixels
| XpmReturnExtensions
);
696 XpmCreatePixmapFromData(
697 display
, wRoot
, screen_xpm
, &_pWindow
, &_pWindowMask
, &_attrWindow
700 fprintf(stderr
, "%s: couldn't create screen buffer.\n", getApplicationName());
704 _gcWindow
= XCreateGC(_display
, _pWindow
, (GCForeground
| GCBackground
), &xgcWindow
);
706 fprintf(stderr
, "%s: couldn't create screen buffer graphics.\n", getApplicationName());
711 void redrawWindow () {
714 int w
= getButtonWidth();
715 int h
= getButtonHeight();
716 for (i
= 0; i
< getButtonCount(); i
++) {
717 int x
, y
, xoff
, yoff
;
720 if (i
== getCurrentScreen()) {
724 getButtonLocation(i
, &x
, &y
);
725 XSetClipMask(_display
, _gcWindow
, _pWindowMask
);
726 XSetClipOrigin(_display
, _gcWindow
, 0, 0);
727 XCopyArea(_display
, _pButtonTheme
, _pWindow
, _gcWindow
, xoff
+ x
- 7, y
- 7, w
, h
, x
, y
);
728 XCopyArea(_display
, _pButtonTheme
, _pWindow
, _gcWindow
, xoff
, 0, w
, 1, x
, y
);
729 XCopyArea(_display
, _pButtonTheme
, _pWindow
, _gcWindow
, xoff
, 0, 1, h
, x
, y
);
730 XCopyArea(_display
, _pButtonTheme
, _pWindow
, _gcWindow
, xoff
+ 50, 0, 1, h
, x
+ w
- 1, y
);
731 XCopyArea(_display
, _pButtonTheme
, _pWindow
, _gcWindow
, xoff
, 50, w
, 1, x
, y
+ h
- 1);
732 XSetClipMask(_display
, _gcWindow
, _pButtonThemeMask
);
733 XSetClipOrigin(_display
, _gcWindow
, (x
+ (w
- 10) / 2) - i
* 10, -51 - yoff
+ y
+ (h
- 10) / 2);
734 XCopyArea(_display
, _pButtonTheme
, _pWindow
, _gcWindow
, i
* 10, 51 + yoff
, 10, 10, x
+ (w
- 10) / 2, y
+ (h
- 10) / 2);
736 while (XCheckTypedWindowEvent(_display
, _wMain
, Expose
, &event
));
737 XCopyArea(_display
, _pWindow
, _wMain
, _gcMain
, 0, 0, 64, 64, 0, 0);
738 while (XCheckTypedWindowEvent(_display
, _wIcon
, Expose
, &event
));
739 XCopyArea(_display
, _pWindow
, _wIcon
, _gcMain
, 0, 0, 64, 64, 0, 0);
742 void getWindowOrigin (Window w
, int* nX
, int* nY
) {
743 Window wWindow
, wParent
, wRoot
;
745 unsigned int nChildren
;
746 unsigned int ww
, wh
, wb
, wd
;
752 if (!XQueryTree(getDisplay(), wParent
, &wRoot
, &wParent
, &wChildren
, &nChildren
)) {
758 } while (wParent
!= wRoot
);
760 if (XGetGeometry(getDisplay(), wWindow
, &wRoot
, &wx
, &wy
, &ww
, &wh
, &wb
, &wd
)) {
774 void loop (long nTooltipShowDelay
, long nTooltipReshowDelay
) {
775 Display
* display
= getDisplay();
777 long nTooltipTimer
= -1, nTooltipHideTimer
= -1, nNow
;
778 int nTooltipButton
, nTooltipX
, nTooltipY
;
781 fprintf(stdout
, "[%8ld] starting event loop\n", currentTimeMillis());
784 while (XPending(display
) || (nTooltipTimer
== -1)) {
785 XNextEvent(display
, &event
);
786 switch (event
.type
) {
788 if (event
.xexpose
.count
== 0) {
792 case ConfigureNotify
:
796 if (hasTooltipSupport()) {
798 nTooltipTimer
= currentTimeMillis();
799 nTooltipX
= event
.xbutton
.x
;
800 nTooltipY
= event
.xbutton
.y
;
801 nTooltipButton
= getButtonAt(event
.xbutton
.x
, event
.xbutton
.y
);
803 int nButton
= getButtonAt(event
.xbutton
.x
, event
.xbutton
.y
);
804 if (nButton
!= nTooltipButton
) {
807 nTooltipX
= event
.xbutton
.x
;
808 nTooltipY
= event
.xbutton
.y
;
809 nTooltipButton
= nButton
;
810 showTooltip(nTooltipButton
, nTooltipX
, nTooltipY
);
818 nTooltipHideTimer
= currentTimeMillis();
824 int nButton
= getButtonAt(event
.xbutton
.x
, event
.xbutton
.y
);
826 fprintf(stdout
, "[%8ld] button %d pressed\n", currentTimeMillis(), nButton
);
831 nTooltipHideTimer
= currentTimeMillis();
833 setCurrentScreen(nButton
);
839 if (strcmp(XA_WIN_WORKSPACE
, XGetAtomName(getDisplay(), event
.xproperty
.atom
)) == 0) {
840 setCurrentScreen(-1);
842 fprintf(stdout
, "[%8ld] new current workspace (%d= %s)\n",
843 currentTimeMillis(), getCurrentScreen(), getScreenName(getCurrentScreen()));
850 fprintf(stdout
, "[%8ld] quit application\n", currentTimeMillis());
860 nNow
= currentTimeMillis();
862 nTooltipTimer
!= -1 &&
864 (nNow
> nTooltipTimer
+ nTooltipShowDelay
) ||
865 (nNow
< nTooltipHideTimer
+ nTooltipReshowDelay
)
868 showTooltip(nTooltipButton
, nTooltipX
, nTooltipY
);
878 static int _nButtons
;
879 static int _nButtonRows
, _nButtonColumns
;
880 static int _nButtonWidth
, _nButtonHeight
;
882 int getButtonCount () {
886 int getButtonRowCount () {
890 int getButtonColumnCount () {
891 return _nButtonColumns
;
894 int getButtonWidth () {
895 return _nButtonWidth
;
898 int getButtonHeight () {
899 return _nButtonHeight
;
902 int getButtonAt (int nLocationX
, int nLocationY
) {
903 int i
, nButtonX
, nButtonY
;
904 for (i
= 0; i
< _nButtons
; i
++) {
905 getButtonLocation(i
, &nButtonX
, &nButtonY
);
907 nLocationX
>= nButtonX
&& nLocationX
< nButtonX
+ _nButtonWidth
&&
908 nLocationY
>= nButtonY
&& nLocationY
< nButtonY
+ _nButtonHeight
916 void getButtonLocation (int nButton
, int* nLocationX
, int* nLocationY
) {
917 if (nButton
< 0 || nButton
> _nButtons
) {
918 *nLocationX
= *nLocationY
= 0;
920 *nLocationX
= *nLocationY
= 7;
921 while (nButton
>= _nButtonColumns
) {
922 *nLocationY
+= _nButtonHeight
+ 3;
923 nButton
-= _nButtonColumns
;
925 while (nButton
> 0) {
926 *nLocationX
+= _nButtonWidth
+ 3;
932 void initButtons (int nButtons
, int nColumns
, int nRows
) {
933 if (nButtons
!= -1) {
936 _nButtons
= getScreenCount();
938 /* only handle nine screens at most */
942 if (nColumns
== -1 || nRows
== -1) {
945 _nButtonRows
= _nButtonColumns
= 1;
954 _nButtonColumns
= _nButtonRows
= 2;
963 _nButtonColumns
= _nButtonRows
= 3;
967 _nButtonColumns
= nColumns
;
970 if (_nButtons
> _nButtonColumns
* _nButtonRows
) {
971 _nButtons
= _nButtonColumns
* _nButtonRows
;
974 fprintf(stdout
, "[%8ld] initializing buttons\n", currentTimeMillis());
975 fprintf(stdout
, "[%8ld] - %d workspace buttons\n", currentTimeMillis(), _nButtons
);
976 fprintf(stdout
, "[%8ld] - button layout %dx%d\n", currentTimeMillis(), _nButtonColumns
, _nButtonRows
);
979 if (_nButtonColumns
== 1) {
981 } else if (_nButtonColumns
== 2) {
987 if (_nButtonRows
== 1) {
989 } else if (_nButtonRows
== 2) {
1001 static struct timeval _tStart
;
1005 fprintf(stdout
, "[ ] initializing time\n");
1007 gettimeofday(&_tStart
, NULL
);
1010 long currentTimeMillis () {
1011 struct timeval tNow
;
1012 struct timeval tElapsed
;
1014 gettimeofday(&tNow
, NULL
);
1016 if (_tStart
.tv_usec
> tNow
.tv_usec
) {
1017 tNow
.tv_usec
+= 1000000;
1020 tElapsed
.tv_sec
= tNow
.tv_sec
- _tStart
.tv_sec
;
1021 tElapsed
.tv_usec
= tNow
.tv_usec
- _tStart
.tv_usec
;
1022 return (tElapsed
.tv_sec
* 1000) + (tElapsed
.tv_usec
/ 1000);
1029 static Atom _xaWinWorkspace
;
1030 static Atom _xaWinWorkspaceNames
;
1031 static int _nScreens
;
1032 static char** _szScreenNames
;
1033 static int _nLastScreen
;
1035 int getScreenCount () {
1039 char* getScreenName (int nScreen
) {
1040 if (nScreen
< 0 || nScreen
>= _nScreens
) {
1043 return _szScreenNames
[nScreen
];
1046 void gotoScreen (int nWorkspace
) {
1048 event
.type
= ClientMessage
;
1049 event
.xclient
.type
= ClientMessage
;
1050 event
.xclient
.window
= getRootWindow();
1051 event
.xclient
.message_type
= _xaWinWorkspace
;
1052 event
.xclient
.format
= 32;
1053 event
.xclient
.data
.l
[0]= nWorkspace
;
1054 event
.xclient
.data
.l
[1]= currentTimeMillis();
1055 XSendEvent(getDisplay(), getRootWindow(), False
, SubstructureNotifyMask
, (XEvent
*) &event
);
1058 int getCurrentScreen () {
1059 return _nLastScreen
;
1062 void setCurrentScreen (int nCurrentScreen
) {
1063 if (nCurrentScreen
== -1) {
1064 long nScreen
= _nLastScreen
;
1067 unsigned long nItems
, nBytesAfter
;
1068 unsigned char* data
;
1071 getDisplay(), getRootWindow(), _xaWinWorkspace
,
1072 0, 8192, False
, XA_CARDINAL
, &xaType
, &nFormat
, &nItems
, &nBytesAfter
, &data
1074 if ((nFormat
== 32) && (nItems
== 1) && (nBytesAfter
== 0)) {
1075 nScreen
= *(long*) data
;
1077 if (xaType
!= None
) {
1080 _nLastScreen
= nScreen
;
1082 _nLastScreen
= nCurrentScreen
;
1086 void initScreens () {
1090 fprintf(stdout
, "[%8ld] initializing window maker communication\n", currentTimeMillis());
1092 _xaWinWorkspace
= XInternAtom(getDisplay(), XA_WIN_WORKSPACE
, False
);
1093 _xaWinWorkspaceNames
= XInternAtom(getDisplay(), XA_WIN_WORKSPACE_NAMES
, False
);
1094 XGetTextProperty(getDisplay(), getRootWindow(), &tp
, _xaWinWorkspaceNames
);
1095 XTextPropertyToStringList(&tp
, &_szScreenNames
, &_nScreens
);
1097 setCurrentScreen(-1);
1098 if (_nLastScreen
== -1) {
1101 "%s: couldn't determine current workspace.\n" \
1102 "Make sure your WindowMaker has Gnome support enabled!\n",
1103 getApplicationName()
1105 setCurrentScreen(0);
1109 fprintf(stdout
, "[%8ld] - %d worspaces found\n", currentTimeMillis(), getScreenCount());
1110 for (i
= 0; i
< getScreenCount(); i
++) {
1111 fprintf(stdout
, "[%8ld] - workspace %d: %s\n", currentTimeMillis(), i
, getScreenName(i
));
1113 fprintf(stdout
, "[%8ld] - current workspace is %d (%s)\n", currentTimeMillis(),
1114 getCurrentScreen(), getScreenName(getCurrentScreen()));
1122 int _bTooltip
= 0, _bTooltipSupport
, _bTooltipOutside
;
1123 XFontStruct
* _fTooltip
;
1124 int _nFontHeight
, _nFontY
;
1125 int _nScreenWidth
, _nScreenHeight
;
1129 int hasTooltipSupport () {
1130 return _bTooltipSupport
;
1133 void showTooltip (int nButton
, int nMouseX
, int nMouseY
) {
1134 Pixmap pixmap
, mask
;
1135 int nMainWinX
, nMainWinY
;
1136 int nButtonX
, nButtonY
, nButtonWidth
, nButtonHeight
;
1137 int nTextY
, nX
, nY
, nWidth
, nHeight
, nSide
;
1140 if (!_bTooltipSupport
) {
1148 fprintf(stdout
, "[%8ld] showing tooltip for button %d (%s) at %d, %d\n", currentTimeMillis(),
1149 nButton
, getScreenName(nButton
), nMouseX
, nMouseY
);
1152 szText
= strdup(getScreenName(nButton
));
1153 nWidth
= XTextWidth(_fTooltip
, szText
, strlen(szText
)) + 16;
1154 nHeight
= _nFontHeight
+ 4;
1158 if (nWidth
< nHeight
) {
1162 fprintf(stdout
, "[%8ld] tooltip size: %d, %d\n", currentTimeMillis(), nWidth
, nHeight
);
1165 getWindowOrigin(getIconWindow(), &nMainWinX
, &nMainWinY
);
1166 if (_bTooltipOutside
) {
1167 nButtonX
= nMainWinX
;
1168 nButtonY
= nMainWinY
;
1172 getButtonLocation(nButton
, &nButtonX
, &nButtonY
);
1173 nButtonX
+= nMainWinX
;
1174 nButtonY
+= nMainWinY
;
1175 nButtonWidth
= getButtonWidth();
1176 nButtonHeight
= getButtonHeight();
1179 if (nButtonX
+ nWidth
> _nScreenWidth
) {
1180 nSide
= TOOLTIP_RIGHT
;
1181 nX
= nButtonX
- nWidth
+ nButtonWidth
/ 2;
1186 nSide
= TOOLTIP_LEFT
;
1187 nX
= nButtonX
+ nButtonWidth
/ 2;
1189 if (nX
+ nWidth
> _nScreenWidth
) {
1190 nX
= _nScreenWidth
- nWidth
;
1193 if (nButtonY
- (nHeight
+ TOOLTIP_SPACE
) < 0) {
1194 nSide
|= TOOLTIP_TOP
;
1195 nY
= nButtonY
+ nButtonHeight
- 1;
1196 nTextY
= TOOLTIP_SPACE
;
1198 nSide
|= TOOLTIP_BOTTOM
;
1199 nY
= nButtonY
- (nHeight
+ TOOLTIP_SPACE
);
1203 pixmap
= createTooltipPixmap(nWidth
, nHeight
, nSide
, &mask
);
1205 XSetForeground(getDisplay(), getMainGraphics(), getBlackPixel());
1206 XSetFont(getDisplay(), getMainGraphics(), _fTooltip
->fid
);
1207 XDrawString(getDisplay(), pixmap
, getMainGraphics(),
1208 8, nTextY
+ (nHeight
- _nFontHeight
) / 2 + _nFontY
, szText
, strlen(szText
));
1210 XSetWindowBackgroundPixmap(getDisplay(), _wTooltip
, pixmap
);
1212 XResizeWindow(getDisplay(), _wTooltip
, nWidth
, nHeight
+ TOOLTIP_SPACE
);
1213 XShapeCombineMask(getDisplay(), _wTooltip
, ShapeBounding
, 0, 0, mask
, ShapeSet
);
1214 XFreePixmap(getDisplay(), mask
);
1215 XMoveWindow(getDisplay(), _wTooltip
, nX
, nY
);
1216 XMapRaised(getDisplay(), _wTooltip
);
1217 XFreePixmap(getDisplay(), pixmap
);
1222 void hideTooltip () {
1223 if (!_bTooltipSupport
) {
1228 fprintf(stdout
, "[%8ld] hiding tooltip\n", currentTimeMillis());
1230 XUnmapWindow(getDisplay(), _wTooltip
);
1236 if (!_bTooltipSupport
) {
1242 void initTooltip (int bTooltipSupport
, char* szFontName
, int bTooltipOutside
) {
1243 XSetWindowAttributes attribs
;
1244 unsigned long vmask
;
1246 _bTooltipSupport
= bTooltipSupport
;
1247 if (!_bTooltipSupport
) {
1249 fprintf(stdout
, "[%8ld] initializing tooltips (disabled)\n", currentTimeMillis());
1254 fprintf(stdout
, "[%8ld] initializing tooltips\n", currentTimeMillis());
1256 _bTooltipOutside
= bTooltipOutside
;
1257 _fTooltip
= XLoadQueryFont(getDisplay(), szFontName
);
1259 fprintf(stderr
, "%s: couldn't allocate font '%s'.\n", getApplicationName(), szFontName
);
1262 _nFontHeight
= _fTooltip
->ascent
+ _fTooltip
->descent
;
1263 _nFontY
= _fTooltip
->ascent
;
1264 _nScreenWidth
= WidthOfScreen(ScreenOfDisplay(getDisplay(), getDefaultScreen()));
1265 _nScreenHeight
= HeightOfScreen(ScreenOfDisplay(getDisplay(), getDefaultScreen()));
1267 fprintf(stdout
, "[%8ld] configuring tooltip font:\n" \
1268 "[%8ld] - '%s'\n[%8ld] - font-height= %d, font-ascent= %d\n" \
1269 "[%8ld] configuring screen size: %dx%d\n",
1270 currentTimeMillis(), currentTimeMillis(), szFontName
, currentTimeMillis(), _nFontHeight
, _nFontY
,
1271 currentTimeMillis(), _nScreenWidth
, _nScreenHeight
1275 vmask
= CWSaveUnder
| CWOverrideRedirect
| CWBorderPixel
;
1276 attribs
.save_under
= True
;
1277 attribs
.override_redirect
= True
;
1278 attribs
.border_pixel
= 0;
1279 _wTooltip
= XCreateWindow(getDisplay(), getRootWindow(), 1, 1, 10, 10, 1,
1280 CopyFromParent
, CopyFromParent
, CopyFromParent
, vmask
, &attribs
);
1282 fprintf(stderr
, "Cannot create tooltip window.\n");
1287 void destroyTooltip () {
1288 if (!_bTooltipSupport
) {
1292 XFreeGC(getDisplay(), _gcMono
);
1295 XDestroyWindow(getDisplay(), _wTooltip
);
1298 void drawTooltipBalloon (Pixmap pix
, GC gc
, int x
, int y
, int w
, int h
, int side
) {
1299 Display
* display
= getDisplay();
1303 XFillArc(display
, pix
, gc
, x
, y
, rad
, rad
, 90*64, 90*64);
1304 XFillArc(display
, pix
, gc
, x
, y
+h
-1-rad
, rad
, rad
, 180*64, 90*64);
1306 XFillArc(display
, pix
, gc
, x
+w
-1-rad
, y
, rad
, rad
, 0*64, 90*64);
1307 XFillArc(display
, pix
, gc
, x
+w
-1-rad
, y
+h
-1-rad
, rad
, rad
, 270*64, 90*64);
1309 XFillRectangle(display
, pix
, gc
, x
, y
+rad
/2, w
, h
-rad
);
1310 XFillRectangle(display
, pix
, gc
, x
+rad
/2, y
, w
-rad
, h
);
1312 if (side
& TOOLTIP_BOTTOM
) {
1314 pt
[1].y
= y
+h
-1+TOOLTIP_SPACE
;
1318 pt
[1].y
= y
-TOOLTIP_SPACE
;
1321 if (side
& TOOLTIP_RIGHT
) {
1322 pt
[0].x
= x
+w
-h
+2*h
/16;
1323 pt
[1].x
= x
+w
-h
+11*h
/16;
1324 pt
[2].x
= x
+w
-h
+7*h
/16;
1326 pt
[0].x
= x
+h
-2*h
/16;
1327 pt
[1].x
= x
+h
-11*h
/16;
1328 pt
[2].x
= x
+h
-7*h
/16;
1330 XFillPolygon(display
, pix
, gc
, pt
, 3, Convex
, CoordModeOrigin
);
1333 Pixmap
createTooltipPixmap (int width
, int height
, int side
, Pixmap
*mask
) {
1334 Display
* display
= getDisplay();
1335 Window wRoot
= getRootWindow();
1336 GC gc
= getMainGraphics();
1341 bitmap
= XCreatePixmap(display
, wRoot
, width
+TOOLTIP_SPACE
, height
+TOOLTIP_SPACE
, 1);
1344 _gcMono
= XCreateGC(display
, bitmap
, 0, NULL
);
1346 XSetForeground(display
, _gcMono
, 0);
1347 XFillRectangle(display
, bitmap
, _gcMono
, 0, 0, width
+TOOLTIP_SPACE
, height
+TOOLTIP_SPACE
);
1349 pixmap
= XCreatePixmap(display
, wRoot
, width
+TOOLTIP_SPACE
, height
+TOOLTIP_SPACE
, getDefaultDepth());
1350 XSetForeground(display
, gc
, getBlackPixel());
1351 XFillRectangle(display
, pixmap
, gc
, 0, 0, width
+TOOLTIP_SPACE
, height
+TOOLTIP_SPACE
);
1353 if (side
& TOOLTIP_BOTTOM
) {
1360 XSetForeground(display
, _gcMono
, 1);
1361 drawTooltipBalloon(bitmap
, _gcMono
, x
, y
, width
, height
, side
);
1362 XSetForeground(display
, gc
, getWhitePixel());
1363 drawTooltipBalloon(pixmap
, gc
, x
+1, y
+1, width
-2, height
-2, side
);