wmpager: use select(2) instead of usleep(3), increase sleep time to 0.5 sec
[dockapps.git] / wmpager / src / wmpager.c
blob7080b63379ae1c8ef7c9eaa53cc7185c1419ab0f
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>
4 * All rights reserved.
5 */
7 #include <X11/Xlib.h>
8 #include <X11/Xutil.h>
9 #include <X11/Xatom.h>
10 #include <X11/xpm.h>
11 #include <X11/extensions/shape.h>
12 #include <sys/time.h>
13 #include <sys/types.h>
14 #include <sys/select.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
21 #include "buttons.xpm"
22 #include "screen.xpm"
25 * Constants
28 #define AUTHOR "Bruno Essmann <essmann@users.sourceforge.net>"
29 #define APPLICATION "wmpager"
30 #define VERSION "1.2"
32 #define XA_NET_NUMBER_OF_DESKTOPS "_NET_NUMBER_OF_DESKTOPS"
33 #define XA_NET_CURRENT_DESKTOP "_NET_CURRENT_DESKTOP"
34 #define XA_NET_DESKTOP_NAMES "_NET_DESKTOP_NAMES"
36 #define WMPAGER_ENV "WMPAGER"
37 #define WMPAGER_DEFAULT_INSTALL_DIR "/usr/local/share/wmpager/"
38 #define WMPAGER_USER_DIR ".wmpager/"
41 * Prototypes
44 void usage (int bVerbose);
45 void info ();
46 void setVerbose (int bVerbose);
47 int isVerbose ();
49 void initApplicationName (char* szApplicationName);
50 char* getApplicationName ();
52 void initDisplay (char* szDisplay);
53 void destroyDisplay ();
54 Display* getDisplay ();
56 Pixel getWhitePixel ();
57 Pixel getBlackPixel ();
58 int getDefaultScreen ();
59 int getDefaultDepth ();
60 void initWindow (int nArgc, char** szArgv);
61 void destroyWindow ();
62 GC getWindowGraphics ();
63 GC getMainGraphics ();
64 void initWindowMask (char* szInstallDir, char* szButtonTheme);
65 void redrawWindow ();
66 void getWindowOrigin (Window w, int* nX, int* nY);
68 void loop ();
70 void initButtons (int nButtons, int nColumns, int nRows);
71 int getButtonCount ();
72 int getButtonRowCount ();
73 int getButtonColumnCount ();
74 int getButtonWidth ();
75 int getButtonHeight ();
76 int getButtonAt (int nLocationX, int nLocationY);
77 void getButtonLocation (int nButton, int* nLocationX, int* nLocationY);
79 void initTime ();
80 long currentTimeMillis ();
82 void initScreens ();
83 int getScreenCount ();
84 char* getScreenName (int nScreen);
85 int getCurrentScreen ();
86 void setCurrentScreen (int nCurrentScreen);
87 void gotoScreen (int nWorkspace);
90 * Main
93 int main (int nArgc, char** szArgv) {
94 char* szDisplay= NULL;
95 char* szTheme= NULL;
96 char* szInstallDir= NULL;
97 int nWorkspaces= -1;
98 int bVerbose= 0;
99 int nSizeX= -1, nSizeY= -1;
100 int i;
101 initApplicationName(szArgv[0]);
102 /* we no longer use the WMPAGER environment variable
103 * szInstallDir= (char*) getenv(WMPAGER_ENV);
104 * instead we simply use a default installation directory
106 szInstallDir= WMPAGER_DEFAULT_INSTALL_DIR;
107 i= 1;
108 while (i < nArgc) {
109 if (strcmp("-h", szArgv[i]) == 0 || strcmp("--help", szArgv[i]) == 0) {
110 usage(1);
111 } else if (strcmp("-v", szArgv[i]) == 0 || strcmp("--verbose", szArgv[i]) == 0) {
112 bVerbose= 1;
113 } else if (strcmp("-w", szArgv[i]) == 0 || strcmp("--workspaces", szArgv[i]) == 0) {
114 i+= 1;
115 if (i < nArgc) {
116 sscanf(szArgv[i], "%d", &nWorkspaces);
117 if (nWorkspaces <= 0 || nWorkspaces > 9) {
118 fprintf(stderr, "%s: illegal number of workspaces '%s' for option '%s' (has to be 1-9)\n\n", getApplicationName(), szArgv[i], szArgv[i-1]);
119 usage(0);
121 } else {
122 fprintf(stderr, "%s: workspace count expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
123 usage(0);
125 } else if (strcmp("-s", szArgv[i]) == 0 || strcmp("--size", szArgv[i]) == 0) {
126 i+= 1;
127 if (i < nArgc) {
128 sscanf(szArgv[i], "%dx%d", &nSizeX, &nSizeY);
129 if (nSizeX <= 0 || nSizeX > 3 || nSizeY <= 0 || nSizeY > 3) {
130 fprintf(stderr, "%s: illegal size '%s' for option '%s' (has to be 1x1 .. 3x3)\n\n", getApplicationName(), szArgv[i], szArgv[i-1]);
131 usage(0);
133 } else {
134 fprintf(stderr, "%s: size argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
135 usage(0);
137 } else if (strcmp("-i", szArgv[i]) == 0 || strcmp("--installdir", szArgv[i]) == 0) {
138 i+= 1;
139 if (i < nArgc) {
140 struct stat buf;
141 szInstallDir= szArgv[i];
142 if (stat(szInstallDir, &buf) != 0) {
143 fprintf(stderr, "%s: cannot access installation directory '%s'\n\n", getApplicationName(), szArgv[i]);
144 usage(0);
146 } else {
147 fprintf(stderr, "%s: display argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
148 usage(0);
150 } else if (strcmp("-d", szArgv[i]) == 0 || strcmp("--display", szArgv[i]) == 0) {
151 i+= 1;
152 if (i < nArgc) {
153 szDisplay= szArgv[i];
154 } else {
155 fprintf(stderr, "%s: display argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
156 usage(0);
158 } else if (strcmp("-t", szArgv[i]) == 0 || strcmp("--theme", szArgv[i]) == 0) {
159 i+= 1;
160 if (i < nArgc) {
161 szTheme= strdup(szArgv[i]);
162 } else {
163 fprintf(stderr, "%s: theme argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
164 usage(0);
166 } else {
167 fprintf(stderr, "%s: unknown option '%s'\n\n", getApplicationName(), szArgv[i]);
168 usage(0);
170 i+= 1;
172 setVerbose(bVerbose);
173 if (isVerbose()) {
174 char* szRealDisplay= (szDisplay == NULL) ? (char*) getenv("DISPLAY") : szDisplay;
175 if (szRealDisplay == NULL) {
176 szRealDisplay= "localhost:0.0";
178 info();
179 fprintf(
180 stdout,
181 "[ ] startup options:\n" \
182 "[ ] - verbose= true\n" \
183 "[ ] - display= '%s'\n" \
184 "[ ] - installdir= '%s'\n" \
185 "[ ] - theme= '%s'\n" \
186 "[ ] - workspaces= '%d'\n" \
187 "[ ] - size= '%dx%d'\n",
188 szRealDisplay,
189 szInstallDir == NULL ? "<undefined>" : szInstallDir,
190 szTheme == NULL ? "<built-in>" : szTheme,
191 nWorkspaces,
192 nSizeX, nSizeY
195 initTime();
196 initDisplay(szDisplay);
197 initWindow(nArgc, szArgv);
198 initScreens();
199 initButtons(nWorkspaces, nSizeX, nSizeY);
200 initWindowMask(szInstallDir, szTheme);
201 loop();
202 return 0;
206 * Verbose
209 static int _bVerbose;
211 void setVerbose (int bVerbose) {
212 _bVerbose= bVerbose;
215 int isVerbose () {
216 return _bVerbose;
220 * Usage
223 #define USAGE \
224 "usage: %s [options]\n\n" \
225 "where options include:\n" \
226 " -h --help display usage and version information\n" \
227 " -v --verbose verbose message output\n" \
228 " -d --display <name> the display to use (defaults to the\n" \
229 " 'DISPLAY' environment variable)\n" \
230 " -s --size <w>x<h> number of buttons (default depends on the\n" \
231 " number of workspaces you have, i.e. 2x2 for 4\n" \
232 " workspaces, 2x3 for 6, maximum is 3x3)\n" \
233 " -w --workspaces <count> number of workspace buttons to display\n" \
234 " (default is the number of workspaces you have,\n" \
235 " maximum is 9)\n" \
236 " -t --theme <theme.xpm> the button theme to use, extension\n" \
237 " '.xpm' is optional, for more information about\n" \
238 " themes see docu (default is the built-in theme)\n" \
239 " -i --installdir <dir> specifies the installation directory location,\n" \
240 " this location is automatically searched for themes\n" \
241 " (defaults to the '/usr/local/share/wmpager/'\n" \
242 " and the user specific '~/.wmpager' directory)\n"
244 void usage (int bVerbose) {
245 if (bVerbose) {
246 info();
247 fprintf(stdout, USAGE, getApplicationName());
248 exit(0);
249 } else {
250 fprintf(stderr, USAGE, getApplicationName());
251 exit(-1);
255 void info () {
256 fprintf(stdout, "%s %s\n\n", APPLICATION, VERSION);
260 * Application
263 static char* _szApplicationName;
265 char* getApplicationName () {
266 return _szApplicationName;
269 void initApplicationName (char* szApplicationName) {
270 if (szApplicationName == NULL) {
271 _szApplicationName= APPLICATION;
272 } else {
273 _szApplicationName= strdup(szApplicationName);
278 * Display
281 static Display* _display;
282 static int _xfd;
284 Display* getDisplay () {
285 return _display;
288 void destroyDisplay () {
289 XCloseDisplay(getDisplay());
292 void initDisplay (char* szDisplay) {
293 if (szDisplay == NULL && ((char*) getenv("DISPLAY")) == NULL) {
294 szDisplay= ":0.0";
296 if (isVerbose()) {
297 char* szRealDisplay= (szDisplay == NULL) ? (char*) getenv("DISPLAY") : szDisplay;
298 if (szRealDisplay == NULL) {
299 szRealDisplay= "localhost:0.0";
301 fprintf(stdout, "[%8ld] initializing display '%s'\n", currentTimeMillis(), szRealDisplay);
303 _display= XOpenDisplay(szDisplay);
304 if (_display == NULL) {
305 fprintf(
306 stderr,
307 "%s: couldn't open display '%s'.\n",
308 getApplicationName(),
309 (szDisplay == NULL) ? ((char*) getenv("DISPLAY")) : szDisplay
311 exit(-1);
313 _xfd= XConnectionNumber(_display);
317 * Window
320 static int _nDefaultScreen, _nDefaultDepth;
321 static Window _wRoot, _wMain, _wIcon;
322 static GC _gcMain, _gcWindow;
323 static XpmAttributes _attrButtonTheme;
324 static Pixmap _pButtonTheme, _pButtonThemeMask;
325 static XpmAttributes _attrWindow;
326 static Pixmap _pWindow, _pWindowMask;
327 static Pixel _pWhite, _pBlack;
329 Pixel getWhitePixel () {
330 return _pWhite;
333 Pixel getBlackPixel () {
334 return _pBlack;
337 int getDefaultScreen () {
338 return _nDefaultScreen;
341 int getDefaultDepth () {
342 return _nDefaultDepth;
345 Window getRootWindow () {
346 return _wRoot;
349 Window getMainWindow () {
350 return _wMain;
353 Window getIconWindow () {
354 return _wIcon;
357 GC getMainGraphics () {
358 return _gcMain;
361 GC getWindowGraphics () {
362 return _gcWindow;
365 void initWindow (int nArgc, char** szArgv) {
366 char* szApplicationName= getApplicationName();
367 Display* display= getDisplay();
368 XSizeHints *xsizehints;
369 XWMHints* xwmhints;
370 XClassHint* xclasshint;
371 XTextProperty xtApplication;
373 if (isVerbose()) {
374 fprintf(stdout, "[%8ld] initializing application window\n", currentTimeMillis());
377 _nDefaultScreen= DefaultScreen(display);
378 _nDefaultDepth= DefaultDepth(display, _nDefaultScreen);
379 _wRoot= RootWindow(display, _nDefaultScreen);
381 XSelectInput(display, _wRoot, PropertyChangeMask);
383 _pWhite= WhitePixel(display, _nDefaultScreen);
384 _pBlack= BlackPixel(display, _nDefaultScreen);
386 xsizehints= XAllocSizeHints();
387 xsizehints->flags= USSize | USPosition;
388 xsizehints->width= xsizehints->height= 64;
390 _wMain= XCreateSimpleWindow(display, _wRoot, 0, 0, 64, 64, 5, _pWhite, _pBlack);
391 if (_wMain == 0) {
392 fprintf(stderr, "Cannot create main window.\n");
393 exit(-1);
396 _wIcon= XCreateSimpleWindow(display, _wMain, 0, 0, 64, 64, 5, _pWhite, _pBlack);
397 if (_wIcon == 0) {
398 fprintf(stderr, "Cannot create icon window.\n");
399 exit(-1);
402 xwmhints= XAllocWMHints();
403 xwmhints->flags= WindowGroupHint | IconWindowHint | StateHint;
404 xwmhints->icon_window= _wIcon;
405 xwmhints->window_group= _wMain;
406 xwmhints->initial_state= WithdrawnState;
407 XSetWMHints(display, _wMain, xwmhints);
409 xclasshint= XAllocClassHint();
410 xclasshint->res_name= APPLICATION;
411 xclasshint->res_class= APPLICATION;
412 XSetClassHint(display, _wMain, xclasshint);
414 XSetWMNormalHints(display, _wMain, xsizehints);
416 XFree(xclasshint);
417 XFree(xwmhints);
418 XFree(xsizehints);
420 if (XStringListToTextProperty(&szApplicationName, 1, &xtApplication) == 0) {
421 fprintf(stderr, "Cannot set window title.\n");
422 exit(-1);
424 XSetWMName(display, _wMain, &xtApplication);
425 XFree(xtApplication.value);
427 _gcMain= XCreateGC(display, _wMain, 0L, NULL);
428 if (_gcMain == NULL) {
429 fprintf(stderr, "Cannot create graphics context.\n");
430 exit(-1);
433 XSelectInput(display, _wMain, ExposureMask | ButtonPressMask | PointerMotionMask | StructureNotifyMask | LeaveWindowMask);
434 XSelectInput(display, _wIcon, ExposureMask | ButtonPressMask | PointerMotionMask | StructureNotifyMask | LeaveWindowMask);
436 XSetCommand(display, _wMain, szArgv, nArgc);
438 XMapWindow(display, _wMain);
441 void destroyWindow () {
442 XFreeGC(getDisplay(), getWindowGraphics());
443 XFreeGC(getDisplay(), getMainGraphics());
444 XDestroyWindow(getDisplay(), getMainWindow());
445 XDestroyWindow(getDisplay(), getIconWindow());
448 void initWindowMask (char* szInstallDir, char* szButtonTheme) {
449 Display* display= getDisplay();
450 GC gc;
451 Window wRoot= getRootWindow();
452 Window wMain= getMainWindow();
453 Window wIcon= getIconWindow();
454 Pixmap pOpaque, pTransparent, pMask;
455 char* mask= (char*) malloc(512);
456 int i;
458 if (isVerbose()) {
459 fprintf(stdout, "[%8ld] initializing window mask\n", currentTimeMillis());
461 for (i= 0; i < 512; i++) {
462 mask[i]= 0x00;
464 pTransparent= XCreateBitmapFromData(display, wRoot, mask, 64, 64);
465 if (pTransparent == 0) {
466 fprintf(stderr, "%s: couldn't create window mask (transparent).\n", getApplicationName());
467 exit(-1);
469 pMask= XCreateBitmapFromData(display, wRoot, mask, 64, 64);
470 if (pMask == 0) {
471 fprintf(stderr, "%s: couldn't create window mask (mask buffer).\n", getApplicationName());
472 exit(-1);
475 for (i= 0; i < 512; i++) {
476 mask[i]= 0xff;
478 pOpaque= XCreateBitmapFromData(display, wRoot, mask, 64, 64);
479 if (pOpaque == 0) {
480 fprintf(stderr, "%s: couldn't create window mask (opaque).\n", getApplicationName());
481 exit(-1);
484 gc= XCreateGC(display, pMask, 0L, NULL);
485 if (gc == NULL) {
486 fprintf(stderr, "%s: couldn't create window mask (mask graphics).\n", getApplicationName());
487 exit(-1);
489 for (i= 0; i < getButtonCount(); i++) {
490 int nButtonX, nButtonY;
491 getButtonLocation(i, &nButtonX, &nButtonY);
492 XCopyArea(display, pOpaque, pMask, gc, nButtonX, nButtonY, getButtonWidth(), getButtonHeight(), nButtonX, nButtonY);
495 free(mask);
496 XFreePixmap(display, pOpaque);
497 XFreePixmap(display, pTransparent);
498 XFreeGC(display, gc);
500 XShapeCombineMask(display, wMain, ShapeBounding, 0, 0, pMask, ShapeSet);
501 XShapeCombineMask(display, wIcon, ShapeBounding, 0, 0, pMask, ShapeSet);
503 if (isVerbose()) {
504 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(),
505 szButtonTheme == NULL ? "<built-in>" : szButtonTheme);
508 _attrButtonTheme.valuemask|= (XpmReturnPixels | XpmReturnExtensions);
509 if (szButtonTheme == NULL) {
510 if (
511 XpmCreatePixmapFromData(
512 display, wRoot, buttons_xpm, &_pButtonTheme, &_pButtonThemeMask, &_attrButtonTheme
513 ) != XpmSuccess
515 fprintf(stderr, "%s: couldn't create button theme.\n", getApplicationName());
516 exit(-1);
518 } else {
519 int bCheckAgain= 0;
520 struct stat buf;
521 /* check for absolute button theme pathname */
522 if (stat(szButtonTheme, &buf) == -1) {
523 char* szNewTheme= (char*) malloc(strlen(szButtonTheme) + 5);
524 strcpy(szNewTheme, szButtonTheme);
525 strcat(szNewTheme, ".xpm");
526 if (isVerbose()) {
527 fprintf(stdout, "[%8ld] theme file '%s' not found, trying '%s'\n", currentTimeMillis(), szButtonTheme, szNewTheme);
529 /* check for absolute button theme pathname (with .xpm added) */
530 if (stat(szNewTheme, &buf) == 0) {
531 free(szButtonTheme);
532 szButtonTheme= szNewTheme;
533 if (isVerbose()) {
534 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme);
536 } else {
537 bCheckAgain= 1;
538 free(szNewTheme);
541 if (bCheckAgain && szInstallDir != NULL) {
542 char* szNewTheme= (char*) malloc(strlen(szInstallDir) + strlen(szButtonTheme) + 6);
543 strcpy(szNewTheme, szInstallDir);
544 if (szNewTheme[strlen(szNewTheme) - 1] != '/') {
545 strcat(szNewTheme, "/");
547 strcat(szNewTheme, szButtonTheme);
548 if (stat(szNewTheme, &buf) == 0) {
549 bCheckAgain= 0;
550 free(szButtonTheme);
551 szButtonTheme= szNewTheme;
552 if (isVerbose()) {
553 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme);
555 } else {
556 strcat(szNewTheme, ".xpm");
557 if (stat(szNewTheme, &buf) == 0) {
558 bCheckAgain= 0;
559 free(szButtonTheme);
560 szButtonTheme= szNewTheme;
561 if (isVerbose()) {
562 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme);
564 } else {
565 free(szNewTheme);
569 if (bCheckAgain) {
570 /* as a goody check the ~/.wmpager directory if it exists */
571 char* szHome= (char*) getenv("HOME");
572 if (szHome) {
573 /* one really shouldn't copy&paste but hey this is a q&d tool */
574 char* szNewTheme= (char*) malloc(strlen(szHome) + strlen(szButtonTheme) + strlen(WMPAGER_USER_DIR) + 6);
575 strcpy(szNewTheme, szHome);
576 if (szNewTheme[strlen(szNewTheme) - 1] != '/') {
577 strcat(szNewTheme, "/");
579 strcat(szNewTheme, WMPAGER_USER_DIR);
580 strcat(szNewTheme, szButtonTheme);
581 if (stat(szNewTheme, &buf) == 0) {
582 bCheckAgain= 0;
583 free(szButtonTheme);
584 szButtonTheme= szNewTheme;
585 if (isVerbose()) {
586 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme);
588 } else {
589 strcat(szNewTheme, ".xpm");
590 if (stat(szNewTheme, &buf) == 0) {
591 bCheckAgain= 0;
592 free(szButtonTheme);
593 szButtonTheme= szNewTheme;
594 if (isVerbose()) {
595 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme);
597 } else {
598 free(szNewTheme);
603 if (
604 XpmReadFileToPixmap(
605 display, wRoot, szButtonTheme, &_pButtonTheme, &_pButtonThemeMask, &_attrButtonTheme
606 ) != XpmSuccess
608 fprintf(stderr, "%s: couldn't read button theme '%s'.\n", getApplicationName(), szButtonTheme);
609 exit(-1);
612 free(szButtonTheme);
615 if (isVerbose()) {
616 fprintf(stdout, "[%8ld] initializing screen buffer\n", currentTimeMillis());
619 _attrWindow.valuemask|= (XpmReturnPixels | XpmReturnExtensions);
620 if (
621 XpmCreatePixmapFromData(
622 display, wRoot, screen_xpm, &_pWindow, &_pWindowMask, &_attrWindow
623 ) != XpmSuccess
625 fprintf(stderr, "%s: couldn't create screen buffer.\n", getApplicationName());
626 exit(-1);
629 _gcWindow= XCreateGC(_display, _pWindow, 0L, NULL);
630 if (_gcWindow == NULL) {
631 fprintf(stderr, "%s: couldn't create screen buffer graphics.\n", getApplicationName());
632 exit(-1);
636 void redrawWindow () {
637 XEvent event;
638 int i;
639 int w= getButtonWidth();
640 int h= getButtonHeight();
641 for (i= 0; i < getButtonCount(); i++) {
642 int x, y, xoff, yoff;
643 xoff= 51;
644 yoff= 10;
645 if (i == getCurrentScreen()) {
646 xoff= 0;
647 yoff= 0;
649 getButtonLocation(i, &x, &y);
650 XSetClipMask(_display, _gcWindow, _pWindowMask);
651 XSetClipOrigin(_display, _gcWindow, 0, 0);
652 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff + x - 7, y - 7, w, h, x, y);
653 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff, 0, w, 1, x, y);
654 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff, 0, 1, h, x, y);
655 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff + 50, 0, 1, h, x + w - 1, y);
656 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff, 50, w, 1, x, y + h - 1);
657 XSetClipMask(_display, _gcWindow, _pButtonThemeMask);
658 XSetClipOrigin(_display, _gcWindow, (x + (w - 10) / 2) - i * 10, -51 - yoff + y + (h - 10) / 2);
659 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, i * 10, 51 + yoff, 10, 10, x + (w - 10) / 2, y + (h - 10) / 2);
661 while (XCheckTypedWindowEvent(_display, _wMain, Expose, &event));
662 XCopyArea(_display, _pWindow, _wMain, _gcMain, 0, 0, 64, 64, 0, 0);
663 while (XCheckTypedWindowEvent(_display, _wIcon, Expose, &event));
664 XCopyArea(_display, _pWindow, _wIcon, _gcMain, 0, 0, 64, 64, 0, 0);
667 void getWindowOrigin (Window w, int* nX, int* nY) {
668 Window wWindow, wParent, wRoot;
669 Window* wChildren;
670 unsigned int nChildren;
671 unsigned int ww, wh, wb, wd;
672 int wx, wy;
674 wParent= w;
675 do {
676 wWindow= wParent;
677 if (!XQueryTree(getDisplay(), wParent, &wRoot, &wParent, &wChildren, &nChildren)) {
678 return;
680 if (wChildren) {
681 XFree(wChildren);
683 } while (wParent != wRoot);
685 if (XGetGeometry(getDisplay(), wWindow, &wRoot, &wx, &wy, &ww, &wh, &wb, &wd)) {
686 if (nX) {
687 *nX= wx;
689 if (nY) {
690 *nY= wy;
696 * Event Loop
699 void loop () {
700 Display* display= getDisplay();
701 XEvent event;
702 char* atom_name;
703 struct timeval tv;
704 fd_set fds;
706 if (isVerbose()) {
707 fprintf(stdout, "[%8ld] starting event loop\n", currentTimeMillis());
709 for (;;) {
710 while (XPending(display)) {
711 XNextEvent(display, &event);
712 switch (event.type) {
713 case Expose:
714 if (event.xexpose.count == 0) {
715 redrawWindow();
717 break;
718 case ConfigureNotify:
719 redrawWindow();
720 break;
721 case ButtonPress:
723 int nButton= getButtonAt(event.xbutton.x, event.xbutton.y);
724 if (isVerbose()) {
725 fprintf(stdout, "[%8ld] button %d pressed\n", currentTimeMillis(), nButton);
727 if (nButton != -1) {
728 setCurrentScreen(nButton);
729 gotoScreen(nButton);
732 break;
733 case PropertyNotify:
734 atom_name = XGetAtomName(getDisplay(), event.xproperty.atom);
735 if (atom_name == NULL)
736 break;
737 if (strcmp(XA_NET_CURRENT_DESKTOP, atom_name) == 0) {
738 setCurrentScreen(-1);
739 if (isVerbose()) {
740 fprintf(stdout, "[%8ld] new current workspace (%d= %s)\n",
741 currentTimeMillis(), getCurrentScreen(), getScreenName(getCurrentScreen()));
743 redrawWindow();
745 XFree(atom_name);
746 break;
747 case DestroyNotify:
748 if (isVerbose()) {
749 fprintf(stdout, "[%8ld] quit application\n", currentTimeMillis());
751 destroyWindow();
752 destroyDisplay();
753 exit(0);
754 break;
758 tv.tv_sec = 0;
759 tv.tv_usec = 500000UL;
760 FD_ZERO(&fds);
761 FD_SET(_xfd, &fds);
762 select(_xfd + 1, &fds, NULL, NULL, &tv);
767 * Button Helpers
770 static int _nButtons;
771 static int _nButtonRows, _nButtonColumns;
772 static int _nButtonWidth, _nButtonHeight;
774 int getButtonCount () {
775 return _nButtons;
778 int getButtonRowCount () {
779 return _nButtonRows;
782 int getButtonColumnCount () {
783 return _nButtonColumns;
786 int getButtonWidth () {
787 return _nButtonWidth;
790 int getButtonHeight () {
791 return _nButtonHeight;
794 int getButtonAt (int nLocationX, int nLocationY) {
795 int i, nButtonX, nButtonY;
796 for (i= 0; i < _nButtons; i++) {
797 getButtonLocation(i, &nButtonX, &nButtonY);
798 if (
799 nLocationX >= nButtonX && nLocationX < nButtonX + _nButtonWidth &&
800 nLocationY >= nButtonY && nLocationY < nButtonY + _nButtonHeight
802 return i;
805 return -1;
808 void getButtonLocation (int nButton, int* nLocationX, int* nLocationY) {
809 if (nButton < 0 || nButton > _nButtons) {
810 *nLocationX= *nLocationY= 0;
811 } else {
812 *nLocationX= *nLocationY= 7;
813 while (nButton >= _nButtonColumns) {
814 *nLocationY+= _nButtonHeight + 3;
815 nButton-= _nButtonColumns;
817 while (nButton > 0) {
818 *nLocationX+= _nButtonWidth + 3;
819 nButton--;
824 void initButtons (int nButtons, int nColumns, int nRows) {
825 if (nButtons != -1) {
826 _nButtons= nButtons;
827 } else {
828 _nButtons= getScreenCount();
829 if (_nButtons > 9) {
830 /* only handle nine screens at most */
831 _nButtons= 9;
834 if (nColumns == -1 || nRows == -1) {
835 switch (_nButtons) {
836 case 1:
837 _nButtonRows= _nButtonColumns= 1;
838 break;
839 case 2:
840 _nButtonColumns= 1;
841 _nButtonRows= 2;
842 break;
843 case 3:
844 /* fallthrough */
845 case 4:
846 _nButtonColumns= _nButtonRows= 2;
847 break;
848 case 5:
849 /* fallthrough */
850 case 6:
851 _nButtonColumns= 2;
852 _nButtonRows= 3;
853 break;
854 default:
855 _nButtonColumns= _nButtonRows= 3;
856 break;
858 } else {
859 _nButtonColumns= nColumns;
860 _nButtonRows= nRows;
862 if (_nButtons > _nButtonColumns * _nButtonRows) {
863 _nButtons= _nButtonColumns * _nButtonRows;
865 if (isVerbose()) {
866 fprintf(stdout, "[%8ld] initializing buttons\n", currentTimeMillis());
867 fprintf(stdout, "[%8ld] - %d workspace buttons\n", currentTimeMillis(), _nButtons);
868 fprintf(stdout, "[%8ld] - button layout %dx%d\n", currentTimeMillis(), _nButtonColumns, _nButtonRows);
871 if (_nButtonColumns == 1) {
872 _nButtonWidth= 51;
873 } else if (_nButtonColumns == 2) {
874 _nButtonWidth= 24;
875 } else {
876 _nButtonWidth= 15;
879 if (_nButtonRows == 1) {
880 _nButtonHeight= 51;
881 } else if (_nButtonRows == 2) {
882 _nButtonHeight= 24;
883 } else {
884 _nButtonHeight= 15;
890 * Time
893 static struct timeval _tStart;
895 void initTime () {
896 if (isVerbose()) {
897 fprintf(stdout, "[ ] initializing time\n");
899 gettimeofday(&_tStart, NULL);
902 long currentTimeMillis () {
903 struct timeval tNow;
904 struct timeval tElapsed;
906 gettimeofday(&tNow, NULL);
908 if (_tStart.tv_usec > tNow.tv_usec) {
909 tNow.tv_usec+= 1000000;
910 tNow.tv_sec--;
912 tElapsed.tv_sec= tNow.tv_sec - _tStart.tv_sec;
913 tElapsed.tv_usec= tNow.tv_usec - _tStart.tv_usec;
914 return (tElapsed.tv_sec * 1000) + (tElapsed.tv_usec / 1000);
918 * Screen Handling
921 static Atom _xaNetNumberOfDesktops;
922 static Atom _xaNetCurrentDesktop;
923 static Atom _xaNetDesktopNames;
924 static int _nScreens;
925 static char** _szScreenNames;
926 static int _nDesktopNames;
927 static int _nLastScreen;
929 int getScreenCount () {
930 return _nScreens;
933 char* getScreenName (int nScreen) {
934 if (nScreen < 0 || nScreen >= _nDesktopNames) {
935 return "<empty>";
937 return _szScreenNames[nScreen];
940 void gotoScreen (int nWorkspace) {
941 XEvent event;
942 event.type= ClientMessage;
943 event.xclient.type= ClientMessage;
944 event.xclient.window= getRootWindow();
945 event.xclient.message_type= _xaNetCurrentDesktop;
946 event.xclient.format= 32;
947 event.xclient.data.l[0]= nWorkspace;
948 event.xclient.data.l[1]= currentTimeMillis();
949 XSendEvent(getDisplay(), getRootWindow(), False, SubstructureNotifyMask, (XEvent*) &event);
952 int getCurrentScreen () {
953 return _nLastScreen;
956 void setCurrentScreen (int nCurrentScreen) {
957 if (nCurrentScreen == -1) {
958 long nScreen= _nLastScreen;
959 Atom xaType;
960 int nFormat;
961 unsigned long nItems, nBytesAfter;
962 unsigned char* data;
964 XGetWindowProperty(
965 getDisplay(), getRootWindow(), _xaNetCurrentDesktop,
966 0, 8192, False, XA_CARDINAL, &xaType, &nFormat, &nItems, &nBytesAfter, &data
968 if ((nFormat == 32) && (nItems == 1) && (nBytesAfter == 0)) {
969 nScreen= *(long*) data;
971 XFree(data);
972 _nLastScreen= nScreen;
973 } else {
974 _nLastScreen= nCurrentScreen;
978 void initScreens () {
979 XTextProperty tp;
980 Atom xaType;
981 int nFormat;
982 unsigned long nItems, nBytesAfter;
983 unsigned char* data;
985 if (isVerbose()) {
986 fprintf(stdout, "[%8ld] initializing window maker communication\n", currentTimeMillis());
988 _xaNetNumberOfDesktops= XInternAtom(getDisplay(), XA_NET_NUMBER_OF_DESKTOPS, False);
989 _xaNetCurrentDesktop= XInternAtom(getDisplay(), XA_NET_CURRENT_DESKTOP, False);
990 _xaNetDesktopNames= XInternAtom(getDisplay(), XA_NET_DESKTOP_NAMES, False);
992 XGetWindowProperty(
993 getDisplay(), getRootWindow(), _xaNetNumberOfDesktops,
994 0, 8192, False, XA_CARDINAL, &xaType, &nFormat, &nItems, &nBytesAfter, &data
996 if ((nFormat == 32) && (nItems == 1) && (nBytesAfter == 0)) {
997 _nScreens= *(long*) data;
999 XFree(data);
1001 XGetTextProperty(getDisplay(), getRootWindow(), &tp, _xaNetDesktopNames);
1002 Xutf8TextPropertyToTextList(getDisplay(), &tp, &_szScreenNames, &_nDesktopNames);
1003 XFree(tp.value);
1005 _nLastScreen= -1;
1006 setCurrentScreen(-1);
1007 if (_nLastScreen == -1) {
1008 fprintf(
1009 stderr,
1010 "%s: couldn't determine current workspace.\n" \
1011 "Make sure your WindowMaker has EWMH support enabled!\n",
1012 getApplicationName()
1014 setCurrentScreen(0);
1016 if (isVerbose()) {
1017 int i;
1018 fprintf(stdout, "[%8ld] - %d worspaces found\n", currentTimeMillis(), getScreenCount());
1019 for (i= 0; i < getScreenCount(); i++) {
1020 fprintf(stdout, "[%8ld] - workspace %d: %s\n", currentTimeMillis(), i, getScreenName(i));
1022 fprintf(stdout, "[%8ld] - current workspace is %d (%s)\n", currentTimeMillis(),
1023 getCurrentScreen(), getScreenName(getCurrentScreen()));