wmpager: fix memory leaks pointed out by valgrind
[dockapps.git] / wmpager / src / wmpager.c
blob8d74e7fb2e4d805a315c653df611b95d5077fdb8
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/stat.h>
15 #include <unistd.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
20 #include "buttons.xpm"
21 #include "screen.xpm"
24 * Constants
27 #define AUTHOR "Bruno Essmann <essmann@users.sourceforge.net>"
28 #define APPLICATION "wmpager"
29 #define VERSION "1.2"
31 #define XA_NET_NUMBER_OF_DESKTOPS "_NET_NUMBER_OF_DESKTOPS"
32 #define XA_NET_CURRENT_DESKTOP "_NET_CURRENT_DESKTOP"
33 #define XA_NET_DESKTOP_NAMES "_NET_DESKTOP_NAMES"
35 #define WMPAGER_ENV "WMPAGER"
36 #define WMPAGER_DEFAULT_INSTALL_DIR "/usr/local/share/wmpager/"
37 #define WMPAGER_USER_DIR ".wmpager/"
40 * Prototypes
43 void usage (int bVerbose);
44 void info ();
45 void setVerbose (int bVerbose);
46 int isVerbose ();
48 void initApplicationName (char* szApplicationName);
49 char* getApplicationName ();
51 void initDisplay (char* szDisplay);
52 void destroyDisplay ();
53 Display* getDisplay ();
55 Pixel getWhitePixel ();
56 Pixel getBlackPixel ();
57 int getDefaultScreen ();
58 int getDefaultDepth ();
59 void initWindow (int nArgc, char** szArgv);
60 void destroyWindow ();
61 GC getWindowGraphics ();
62 GC getMainGraphics ();
63 void initWindowMask (char* szInstallDir, char* szButtonTheme);
64 void redrawWindow ();
65 void getWindowOrigin (Window w, int* nX, int* nY);
67 void loop ();
69 void initButtons (int nButtons, int nColumns, int nRows);
70 int getButtonCount ();
71 int getButtonRowCount ();
72 int getButtonColumnCount ();
73 int getButtonWidth ();
74 int getButtonHeight ();
75 int getButtonAt (int nLocationX, int nLocationY);
76 void getButtonLocation (int nButton, int* nLocationX, int* nLocationY);
78 void initTime ();
79 long currentTimeMillis ();
81 void initScreens ();
82 int getScreenCount ();
83 char* getScreenName (int nScreen);
84 int getCurrentScreen ();
85 void setCurrentScreen (int nCurrentScreen);
86 void gotoScreen (int nWorkspace);
89 * Main
92 int main (int nArgc, char** szArgv) {
93 char* szDisplay= NULL;
94 char* szTheme= NULL;
95 char* szInstallDir= NULL;
96 int nWorkspaces= -1;
97 int bVerbose= 0;
98 int nSizeX= -1, nSizeY= -1;
99 int i;
100 initApplicationName(szArgv[0]);
101 /* we no longer use the WMPAGER environment variable
102 * szInstallDir= (char*) getenv(WMPAGER_ENV);
103 * instead we simply use a default installation directory
105 szInstallDir= WMPAGER_DEFAULT_INSTALL_DIR;
106 i= 1;
107 while (i < nArgc) {
108 if (strcmp("-h", szArgv[i]) == 0 || strcmp("--help", szArgv[i]) == 0) {
109 usage(1);
110 } else if (strcmp("-v", szArgv[i]) == 0 || strcmp("--verbose", szArgv[i]) == 0) {
111 bVerbose= 1;
112 } else if (strcmp("-w", szArgv[i]) == 0 || strcmp("--workspaces", szArgv[i]) == 0) {
113 i+= 1;
114 if (i < nArgc) {
115 sscanf(szArgv[i], "%d", &nWorkspaces);
116 if (nWorkspaces <= 0 || nWorkspaces > 9) {
117 fprintf(stderr, "%s: illegal number of workspaces '%s' for option '%s' (has to be 1-9)\n\n", getApplicationName(), szArgv[i], szArgv[i-1]);
118 usage(0);
120 } else {
121 fprintf(stderr, "%s: workspace count expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
122 usage(0);
124 } else if (strcmp("-s", szArgv[i]) == 0 || strcmp("--size", szArgv[i]) == 0) {
125 i+= 1;
126 if (i < nArgc) {
127 sscanf(szArgv[i], "%dx%d", &nSizeX, &nSizeY);
128 if (nSizeX <= 0 || nSizeX > 3 || nSizeY <= 0 || nSizeY > 3) {
129 fprintf(stderr, "%s: illegal size '%s' for option '%s' (has to be 1x1 .. 3x3)\n\n", getApplicationName(), szArgv[i], szArgv[i-1]);
130 usage(0);
132 } else {
133 fprintf(stderr, "%s: size argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
134 usage(0);
136 } else if (strcmp("-i", szArgv[i]) == 0 || strcmp("--installdir", szArgv[i]) == 0) {
137 i+= 1;
138 if (i < nArgc) {
139 struct stat buf;
140 szInstallDir= szArgv[i];
141 if (stat(szInstallDir, &buf) != 0) {
142 fprintf(stderr, "%s: cannot access installation directory '%s'\n\n", getApplicationName(), szArgv[i]);
143 usage(0);
145 } else {
146 fprintf(stderr, "%s: display argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
147 usage(0);
149 } else if (strcmp("-d", szArgv[i]) == 0 || strcmp("--display", szArgv[i]) == 0) {
150 i+= 1;
151 if (i < nArgc) {
152 szDisplay= szArgv[i];
153 } else {
154 fprintf(stderr, "%s: display argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
155 usage(0);
157 } else if (strcmp("-t", szArgv[i]) == 0 || strcmp("--theme", szArgv[i]) == 0) {
158 i+= 1;
159 if (i < nArgc) {
160 szTheme= strdup(szArgv[i]);
161 } else {
162 fprintf(stderr, "%s: theme argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
163 usage(0);
165 } else {
166 fprintf(stderr, "%s: unknown option '%s'\n\n", getApplicationName(), szArgv[i]);
167 usage(0);
169 i+= 1;
171 setVerbose(bVerbose);
172 if (isVerbose()) {
173 char* szRealDisplay= (szDisplay == NULL) ? (char*) getenv("DISPLAY") : szDisplay;
174 if (szRealDisplay == NULL) {
175 szRealDisplay= "localhost:0.0";
177 info();
178 fprintf(
179 stdout,
180 "[ ] startup options:\n" \
181 "[ ] - verbose= true\n" \
182 "[ ] - display= '%s'\n" \
183 "[ ] - installdir= '%s'\n" \
184 "[ ] - theme= '%s'\n" \
185 "[ ] - workspaces= '%d'\n" \
186 "[ ] - size= '%dx%d'\n",
187 szRealDisplay,
188 szInstallDir == NULL ? "<undefined>" : szInstallDir,
189 szTheme == NULL ? "<built-in>" : szTheme,
190 nWorkspaces,
191 nSizeX, nSizeY
194 initTime();
195 initDisplay(szDisplay);
196 initWindow(nArgc, szArgv);
197 initScreens();
198 initButtons(nWorkspaces, nSizeX, nSizeY);
199 initWindowMask(szInstallDir, szTheme);
200 loop();
201 return 0;
205 * Verbose
208 static int _bVerbose;
210 void setVerbose (int bVerbose) {
211 _bVerbose= bVerbose;
214 int isVerbose () {
215 return _bVerbose;
219 * Usage
222 #define USAGE \
223 "usage: %s [options]\n\n" \
224 "where options include:\n" \
225 " -h --help display usage and version information\n" \
226 " -v --verbose verbose message output\n" \
227 " -d --display <name> the display to use (defaults to the\n" \
228 " 'DISPLAY' environment variable)\n" \
229 " -s --size <w>x<h> number of buttons (default depends on the\n" \
230 " number of workspaces you have, i.e. 2x2 for 4\n" \
231 " workspaces, 2x3 for 6, maximum is 3x3)\n" \
232 " -w --workspaces <count> number of workspace buttons to display\n" \
233 " (default is the number of workspaces you have,\n" \
234 " maximum is 9)\n" \
235 " -t --theme <theme.xpm> the button theme to use, extension\n" \
236 " '.xpm' is optional, for more information about\n" \
237 " themes see docu (default is the built-in theme)\n" \
238 " -i --installdir <dir> specifies the installation directory location,\n" \
239 " this location is automatically searched for themes\n" \
240 " (defaults to the '/usr/local/share/wmpager/'\n" \
241 " and the user specific '~/.wmpager' directory)\n"
243 void usage (int bVerbose) {
244 if (bVerbose) {
245 info();
246 fprintf(stdout, USAGE, getApplicationName());
247 exit(0);
248 } else {
249 fprintf(stderr, USAGE, getApplicationName());
250 exit(-1);
254 void info () {
255 fprintf(stdout, "%s %s\n\n", APPLICATION, VERSION);
259 * Application
262 static char* _szApplicationName;
264 char* getApplicationName () {
265 return _szApplicationName;
268 void initApplicationName (char* szApplicationName) {
269 if (szApplicationName == NULL) {
270 _szApplicationName= APPLICATION;
271 } else {
272 _szApplicationName= strdup(szApplicationName);
277 * Display
280 static Display* _display;
282 Display* getDisplay () {
283 return _display;
286 void destroyDisplay () {
287 XCloseDisplay(getDisplay());
290 void initDisplay (char* szDisplay) {
291 if (szDisplay == NULL && ((char*) getenv("DISPLAY")) == NULL) {
292 szDisplay= ":0.0";
294 if (isVerbose()) {
295 char* szRealDisplay= (szDisplay == NULL) ? (char*) getenv("DISPLAY") : szDisplay;
296 if (szRealDisplay == NULL) {
297 szRealDisplay= "localhost:0.0";
299 fprintf(stdout, "[%8ld] initializing display '%s'\n", currentTimeMillis(), szRealDisplay);
301 _display= XOpenDisplay(szDisplay);
302 if (_display == NULL) {
303 fprintf(
304 stderr,
305 "%s: couldn't open display '%s'.\n",
306 getApplicationName(),
307 (szDisplay == NULL) ? ((char*) getenv("DISPLAY")) : szDisplay
309 exit(-1);
314 * Window
317 static int _nDefaultScreen, _nDefaultDepth;
318 static Window _wRoot, _wMain, _wIcon;
319 static GC _gcMain, _gcWindow;
320 static XpmAttributes _attrButtonTheme;
321 static Pixmap _pButtonTheme, _pButtonThemeMask;
322 static XpmAttributes _attrWindow;
323 static Pixmap _pWindow, _pWindowMask;
324 static Pixel _pWhite, _pBlack;
326 Pixel getWhitePixel () {
327 return _pWhite;
330 Pixel getBlackPixel () {
331 return _pBlack;
334 int getDefaultScreen () {
335 return _nDefaultScreen;
338 int getDefaultDepth () {
339 return _nDefaultDepth;
342 Window getRootWindow () {
343 return _wRoot;
346 Window getMainWindow () {
347 return _wMain;
350 Window getIconWindow () {
351 return _wIcon;
354 GC getMainGraphics () {
355 return _gcMain;
358 GC getWindowGraphics () {
359 return _gcWindow;
362 void initWindow (int nArgc, char** szArgv) {
363 char* szApplicationName= getApplicationName();
364 Display* display= getDisplay();
365 XSizeHints *xsizehints;
366 XWMHints* xwmhints;
367 XClassHint* xclasshint;
368 XTextProperty xtApplication;
370 if (isVerbose()) {
371 fprintf(stdout, "[%8ld] initializing application window\n", currentTimeMillis());
374 _nDefaultScreen= DefaultScreen(display);
375 _nDefaultDepth= DefaultDepth(display, _nDefaultScreen);
376 _wRoot= RootWindow(display, _nDefaultScreen);
378 XSelectInput(display, _wRoot, PropertyChangeMask);
380 _pWhite= WhitePixel(display, _nDefaultScreen);
381 _pBlack= BlackPixel(display, _nDefaultScreen);
383 xsizehints= XAllocSizeHints();
384 xsizehints->flags= USSize | USPosition;
385 xsizehints->width= xsizehints->height= 64;
387 _wMain= XCreateSimpleWindow(display, _wRoot, 0, 0, 64, 64, 5, _pWhite, _pBlack);
388 if (_wMain == 0) {
389 fprintf(stderr, "Cannot create main window.\n");
390 exit(-1);
393 _wIcon= XCreateSimpleWindow(display, _wMain, 0, 0, 64, 64, 5, _pWhite, _pBlack);
394 if (_wIcon == 0) {
395 fprintf(stderr, "Cannot create icon window.\n");
396 exit(-1);
399 xwmhints= XAllocWMHints();
400 xwmhints->flags= WindowGroupHint | IconWindowHint | StateHint;
401 xwmhints->icon_window= _wIcon;
402 xwmhints->window_group= _wMain;
403 xwmhints->initial_state= WithdrawnState;
404 XSetWMHints(display, _wMain, xwmhints);
406 xclasshint= XAllocClassHint();
407 xclasshint->res_name= APPLICATION;
408 xclasshint->res_class= APPLICATION;
409 XSetClassHint(display, _wMain, xclasshint);
411 XSetWMNormalHints(display, _wMain, xsizehints);
413 XFree(xclasshint);
414 XFree(xwmhints);
415 XFree(xsizehints);
417 if (XStringListToTextProperty(&szApplicationName, 1, &xtApplication) == 0) {
418 fprintf(stderr, "Cannot set window title.\n");
419 exit(-1);
421 XSetWMName(display, _wMain, &xtApplication);
422 XFree(xtApplication.value);
424 _gcMain= XCreateGC(display, _wMain, 0L, NULL);
425 if (_gcMain == NULL) {
426 fprintf(stderr, "Cannot create graphics context.\n");
427 exit(-1);
430 XSelectInput(display, _wMain, ExposureMask | ButtonPressMask | PointerMotionMask | StructureNotifyMask | LeaveWindowMask);
431 XSelectInput(display, _wIcon, ExposureMask | ButtonPressMask | PointerMotionMask | StructureNotifyMask | LeaveWindowMask);
433 XSetCommand(display, _wMain, szArgv, nArgc);
435 XMapWindow(display, _wMain);
438 void destroyWindow () {
439 XFreeGC(getDisplay(), getWindowGraphics());
440 XFreeGC(getDisplay(), getMainGraphics());
441 XDestroyWindow(getDisplay(), getMainWindow());
442 XDestroyWindow(getDisplay(), getIconWindow());
445 void initWindowMask (char* szInstallDir, char* szButtonTheme) {
446 Display* display= getDisplay();
447 GC gc;
448 Window wRoot= getRootWindow();
449 Window wMain= getMainWindow();
450 Window wIcon= getIconWindow();
451 Pixmap pOpaque, pTransparent, pMask;
452 char* mask= (char*) malloc(512);
453 int i;
455 if (isVerbose()) {
456 fprintf(stdout, "[%8ld] initializing window mask\n", currentTimeMillis());
458 for (i= 0; i < 512; i++) {
459 mask[i]= 0x00;
461 pTransparent= XCreateBitmapFromData(display, wRoot, mask, 64, 64);
462 if (pTransparent == 0) {
463 fprintf(stderr, "%s: couldn't create window mask (transparent).\n", getApplicationName());
464 exit(-1);
466 pMask= XCreateBitmapFromData(display, wRoot, mask, 64, 64);
467 if (pMask == 0) {
468 fprintf(stderr, "%s: couldn't create window mask (mask buffer).\n", getApplicationName());
469 exit(-1);
472 for (i= 0; i < 512; i++) {
473 mask[i]= 0xff;
475 pOpaque= XCreateBitmapFromData(display, wRoot, mask, 64, 64);
476 if (pOpaque == 0) {
477 fprintf(stderr, "%s: couldn't create window mask (opaque).\n", getApplicationName());
478 exit(-1);
481 gc= XCreateGC(display, pMask, 0L, NULL);
482 if (gc == NULL) {
483 fprintf(stderr, "%s: couldn't create window mask (mask graphics).\n", getApplicationName());
484 exit(-1);
486 for (i= 0; i < getButtonCount(); i++) {
487 int nButtonX, nButtonY;
488 getButtonLocation(i, &nButtonX, &nButtonY);
489 XCopyArea(display, pOpaque, pMask, gc, nButtonX, nButtonY, getButtonWidth(), getButtonHeight(), nButtonX, nButtonY);
492 free(mask);
493 XFreePixmap(display, pOpaque);
494 XFreePixmap(display, pTransparent);
495 XFreeGC(display, gc);
497 XShapeCombineMask(display, wMain, ShapeBounding, 0, 0, pMask, ShapeSet);
498 XShapeCombineMask(display, wIcon, ShapeBounding, 0, 0, pMask, ShapeSet);
500 if (isVerbose()) {
501 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(),
502 szButtonTheme == NULL ? "<built-in>" : szButtonTheme);
505 _attrButtonTheme.valuemask|= (XpmReturnPixels | XpmReturnExtensions);
506 if (szButtonTheme == NULL) {
507 if (
508 XpmCreatePixmapFromData(
509 display, wRoot, buttons_xpm, &_pButtonTheme, &_pButtonThemeMask, &_attrButtonTheme
510 ) != XpmSuccess
512 fprintf(stderr, "%s: couldn't create button theme.\n", getApplicationName());
513 exit(-1);
515 } else {
516 int bCheckAgain= 0;
517 struct stat buf;
518 /* check for absolute button theme pathname */
519 if (stat(szButtonTheme, &buf) == -1) {
520 char* szNewTheme= (char*) malloc(strlen(szButtonTheme) + 5);
521 strcpy(szNewTheme, szButtonTheme);
522 strcat(szNewTheme, ".xpm");
523 if (isVerbose()) {
524 fprintf(stdout, "[%8ld] theme file '%s' not found, trying '%s'\n", currentTimeMillis(), szButtonTheme, szNewTheme);
526 /* check for absolute button theme pathname (with .xpm added) */
527 if (stat(szNewTheme, &buf) == 0) {
528 free(szButtonTheme);
529 szButtonTheme= szNewTheme;
530 if (isVerbose()) {
531 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme);
533 } else {
534 bCheckAgain= 1;
535 free(szNewTheme);
538 if (bCheckAgain && szInstallDir != NULL) {
539 char* szNewTheme= (char*) malloc(strlen(szInstallDir) + strlen(szButtonTheme) + 6);
540 strcpy(szNewTheme, szInstallDir);
541 if (szNewTheme[strlen(szNewTheme) - 1] != '/') {
542 strcat(szNewTheme, "/");
544 strcat(szNewTheme, szButtonTheme);
545 if (stat(szNewTheme, &buf) == 0) {
546 bCheckAgain= 0;
547 free(szButtonTheme);
548 szButtonTheme= szNewTheme;
549 if (isVerbose()) {
550 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme);
552 } else {
553 strcat(szNewTheme, ".xpm");
554 if (stat(szNewTheme, &buf) == 0) {
555 bCheckAgain= 0;
556 free(szButtonTheme);
557 szButtonTheme= szNewTheme;
558 if (isVerbose()) {
559 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme);
561 } else {
562 free(szNewTheme);
566 if (bCheckAgain) {
567 /* as a goody check the ~/.wmpager directory if it exists */
568 char* szHome= (char*) getenv("HOME");
569 if (szHome) {
570 /* one really shouldn't copy&paste but hey this is a q&d tool */
571 char* szNewTheme= (char*) malloc(strlen(szHome) + strlen(szButtonTheme) + strlen(WMPAGER_USER_DIR) + 6);
572 strcpy(szNewTheme, szHome);
573 if (szNewTheme[strlen(szNewTheme) - 1] != '/') {
574 strcat(szNewTheme, "/");
576 strcat(szNewTheme, WMPAGER_USER_DIR);
577 strcat(szNewTheme, szButtonTheme);
578 if (stat(szNewTheme, &buf) == 0) {
579 bCheckAgain= 0;
580 free(szButtonTheme);
581 szButtonTheme= szNewTheme;
582 if (isVerbose()) {
583 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme);
585 } else {
586 strcat(szNewTheme, ".xpm");
587 if (stat(szNewTheme, &buf) == 0) {
588 bCheckAgain= 0;
589 free(szButtonTheme);
590 szButtonTheme= szNewTheme;
591 if (isVerbose()) {
592 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme);
594 } else {
595 free(szNewTheme);
600 if (
601 XpmReadFileToPixmap(
602 display, wRoot, szButtonTheme, &_pButtonTheme, &_pButtonThemeMask, &_attrButtonTheme
603 ) != XpmSuccess
605 fprintf(stderr, "%s: couldn't read button theme '%s'.\n", getApplicationName(), szButtonTheme);
606 exit(-1);
609 free(szButtonTheme);
612 if (isVerbose()) {
613 fprintf(stdout, "[%8ld] initializing screen buffer\n", currentTimeMillis());
616 _attrWindow.valuemask|= (XpmReturnPixels | XpmReturnExtensions);
617 if (
618 XpmCreatePixmapFromData(
619 display, wRoot, screen_xpm, &_pWindow, &_pWindowMask, &_attrWindow
620 ) != XpmSuccess
622 fprintf(stderr, "%s: couldn't create screen buffer.\n", getApplicationName());
623 exit(-1);
626 _gcWindow= XCreateGC(_display, _pWindow, 0L, NULL);
627 if (_gcWindow == NULL) {
628 fprintf(stderr, "%s: couldn't create screen buffer graphics.\n", getApplicationName());
629 exit(-1);
633 void redrawWindow () {
634 XEvent event;
635 int i;
636 int w= getButtonWidth();
637 int h= getButtonHeight();
638 for (i= 0; i < getButtonCount(); i++) {
639 int x, y, xoff, yoff;
640 xoff= 51;
641 yoff= 10;
642 if (i == getCurrentScreen()) {
643 xoff= 0;
644 yoff= 0;
646 getButtonLocation(i, &x, &y);
647 XSetClipMask(_display, _gcWindow, _pWindowMask);
648 XSetClipOrigin(_display, _gcWindow, 0, 0);
649 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff + x - 7, y - 7, w, h, x, y);
650 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff, 0, w, 1, x, y);
651 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff, 0, 1, h, x, y);
652 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff + 50, 0, 1, h, x + w - 1, y);
653 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff, 50, w, 1, x, y + h - 1);
654 XSetClipMask(_display, _gcWindow, _pButtonThemeMask);
655 XSetClipOrigin(_display, _gcWindow, (x + (w - 10) / 2) - i * 10, -51 - yoff + y + (h - 10) / 2);
656 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, i * 10, 51 + yoff, 10, 10, x + (w - 10) / 2, y + (h - 10) / 2);
658 while (XCheckTypedWindowEvent(_display, _wMain, Expose, &event));
659 XCopyArea(_display, _pWindow, _wMain, _gcMain, 0, 0, 64, 64, 0, 0);
660 while (XCheckTypedWindowEvent(_display, _wIcon, Expose, &event));
661 XCopyArea(_display, _pWindow, _wIcon, _gcMain, 0, 0, 64, 64, 0, 0);
664 void getWindowOrigin (Window w, int* nX, int* nY) {
665 Window wWindow, wParent, wRoot;
666 Window* wChildren;
667 unsigned int nChildren;
668 unsigned int ww, wh, wb, wd;
669 int wx, wy;
671 wParent= w;
672 do {
673 wWindow= wParent;
674 if (!XQueryTree(getDisplay(), wParent, &wRoot, &wParent, &wChildren, &nChildren)) {
675 return;
677 if (wChildren) {
678 XFree(wChildren);
680 } while (wParent != wRoot);
682 if (XGetGeometry(getDisplay(), wWindow, &wRoot, &wx, &wy, &ww, &wh, &wb, &wd)) {
683 if (nX) {
684 *nX= wx;
686 if (nY) {
687 *nY= wy;
693 * Event Loop
696 void loop () {
697 Display* display= getDisplay();
698 XEvent event;
699 char* atom_name;
701 if (isVerbose()) {
702 fprintf(stdout, "[%8ld] starting event loop\n", currentTimeMillis());
704 for (;;) {
705 while (XPending(display)) {
706 XNextEvent(display, &event);
707 switch (event.type) {
708 case Expose:
709 if (event.xexpose.count == 0) {
710 redrawWindow();
712 break;
713 case ConfigureNotify:
714 redrawWindow();
715 break;
716 case ButtonPress:
718 int nButton= getButtonAt(event.xbutton.x, event.xbutton.y);
719 if (isVerbose()) {
720 fprintf(stdout, "[%8ld] button %d pressed\n", currentTimeMillis(), nButton);
722 if (nButton != -1) {
723 setCurrentScreen(nButton);
724 gotoScreen(nButton);
727 break;
728 case PropertyNotify:
729 atom_name = XGetAtomName(getDisplay(), event.xproperty.atom);
730 if (atom_name == NULL)
731 break;
732 if (strcmp(XA_NET_CURRENT_DESKTOP, atom_name) == 0) {
733 setCurrentScreen(-1);
734 if (isVerbose()) {
735 fprintf(stdout, "[%8ld] new current workspace (%d= %s)\n",
736 currentTimeMillis(), getCurrentScreen(), getScreenName(getCurrentScreen()));
738 redrawWindow();
740 XFree(atom_name);
741 break;
742 case DestroyNotify:
743 if (isVerbose()) {
744 fprintf(stdout, "[%8ld] quit application\n", currentTimeMillis());
746 destroyWindow();
747 destroyDisplay();
748 exit(0);
749 break;
752 usleep(50000);
757 * Button Helpers
760 static int _nButtons;
761 static int _nButtonRows, _nButtonColumns;
762 static int _nButtonWidth, _nButtonHeight;
764 int getButtonCount () {
765 return _nButtons;
768 int getButtonRowCount () {
769 return _nButtonRows;
772 int getButtonColumnCount () {
773 return _nButtonColumns;
776 int getButtonWidth () {
777 return _nButtonWidth;
780 int getButtonHeight () {
781 return _nButtonHeight;
784 int getButtonAt (int nLocationX, int nLocationY) {
785 int i, nButtonX, nButtonY;
786 for (i= 0; i < _nButtons; i++) {
787 getButtonLocation(i, &nButtonX, &nButtonY);
788 if (
789 nLocationX >= nButtonX && nLocationX < nButtonX + _nButtonWidth &&
790 nLocationY >= nButtonY && nLocationY < nButtonY + _nButtonHeight
792 return i;
795 return -1;
798 void getButtonLocation (int nButton, int* nLocationX, int* nLocationY) {
799 if (nButton < 0 || nButton > _nButtons) {
800 *nLocationX= *nLocationY= 0;
801 } else {
802 *nLocationX= *nLocationY= 7;
803 while (nButton >= _nButtonColumns) {
804 *nLocationY+= _nButtonHeight + 3;
805 nButton-= _nButtonColumns;
807 while (nButton > 0) {
808 *nLocationX+= _nButtonWidth + 3;
809 nButton--;
814 void initButtons (int nButtons, int nColumns, int nRows) {
815 if (nButtons != -1) {
816 _nButtons= nButtons;
817 } else {
818 _nButtons= getScreenCount();
819 if (_nButtons > 9) {
820 /* only handle nine screens at most */
821 _nButtons= 9;
824 if (nColumns == -1 || nRows == -1) {
825 switch (_nButtons) {
826 case 1:
827 _nButtonRows= _nButtonColumns= 1;
828 break;
829 case 2:
830 _nButtonColumns= 1;
831 _nButtonRows= 2;
832 break;
833 case 3:
834 /* fallthrough */
835 case 4:
836 _nButtonColumns= _nButtonRows= 2;
837 break;
838 case 5:
839 /* fallthrough */
840 case 6:
841 _nButtonColumns= 2;
842 _nButtonRows= 3;
843 break;
844 default:
845 _nButtonColumns= _nButtonRows= 3;
846 break;
848 } else {
849 _nButtonColumns= nColumns;
850 _nButtonRows= nRows;
852 if (_nButtons > _nButtonColumns * _nButtonRows) {
853 _nButtons= _nButtonColumns * _nButtonRows;
855 if (isVerbose()) {
856 fprintf(stdout, "[%8ld] initializing buttons\n", currentTimeMillis());
857 fprintf(stdout, "[%8ld] - %d workspace buttons\n", currentTimeMillis(), _nButtons);
858 fprintf(stdout, "[%8ld] - button layout %dx%d\n", currentTimeMillis(), _nButtonColumns, _nButtonRows);
861 if (_nButtonColumns == 1) {
862 _nButtonWidth= 51;
863 } else if (_nButtonColumns == 2) {
864 _nButtonWidth= 24;
865 } else {
866 _nButtonWidth= 15;
869 if (_nButtonRows == 1) {
870 _nButtonHeight= 51;
871 } else if (_nButtonRows == 2) {
872 _nButtonHeight= 24;
873 } else {
874 _nButtonHeight= 15;
880 * Time
883 static struct timeval _tStart;
885 void initTime () {
886 if (isVerbose()) {
887 fprintf(stdout, "[ ] initializing time\n");
889 gettimeofday(&_tStart, NULL);
892 long currentTimeMillis () {
893 struct timeval tNow;
894 struct timeval tElapsed;
896 gettimeofday(&tNow, NULL);
898 if (_tStart.tv_usec > tNow.tv_usec) {
899 tNow.tv_usec+= 1000000;
900 tNow.tv_sec--;
902 tElapsed.tv_sec= tNow.tv_sec - _tStart.tv_sec;
903 tElapsed.tv_usec= tNow.tv_usec - _tStart.tv_usec;
904 return (tElapsed.tv_sec * 1000) + (tElapsed.tv_usec / 1000);
908 * Screen Handling
911 static Atom _xaNetNumberOfDesktops;
912 static Atom _xaNetCurrentDesktop;
913 static Atom _xaNetDesktopNames;
914 static int _nScreens;
915 static char** _szScreenNames;
916 static int _nDesktopNames;
917 static int _nLastScreen;
919 int getScreenCount () {
920 return _nScreens;
923 char* getScreenName (int nScreen) {
924 if (nScreen < 0 || nScreen >= _nDesktopNames) {
925 return "<empty>";
927 return _szScreenNames[nScreen];
930 void gotoScreen (int nWorkspace) {
931 XEvent event;
932 event.type= ClientMessage;
933 event.xclient.type= ClientMessage;
934 event.xclient.window= getRootWindow();
935 event.xclient.message_type= _xaNetCurrentDesktop;
936 event.xclient.format= 32;
937 event.xclient.data.l[0]= nWorkspace;
938 event.xclient.data.l[1]= currentTimeMillis();
939 XSendEvent(getDisplay(), getRootWindow(), False, SubstructureNotifyMask, (XEvent*) &event);
942 int getCurrentScreen () {
943 return _nLastScreen;
946 void setCurrentScreen (int nCurrentScreen) {
947 if (nCurrentScreen == -1) {
948 long nScreen= _nLastScreen;
949 Atom xaType;
950 int nFormat;
951 unsigned long nItems, nBytesAfter;
952 unsigned char* data;
954 XGetWindowProperty(
955 getDisplay(), getRootWindow(), _xaNetCurrentDesktop,
956 0, 8192, False, XA_CARDINAL, &xaType, &nFormat, &nItems, &nBytesAfter, &data
958 if ((nFormat == 32) && (nItems == 1) && (nBytesAfter == 0)) {
959 nScreen= *(long*) data;
961 XFree(data);
962 _nLastScreen= nScreen;
963 } else {
964 _nLastScreen= nCurrentScreen;
968 void initScreens () {
969 XTextProperty tp;
970 Atom xaType;
971 int nFormat;
972 unsigned long nItems, nBytesAfter;
973 unsigned char* data;
975 if (isVerbose()) {
976 fprintf(stdout, "[%8ld] initializing window maker communication\n", currentTimeMillis());
978 _xaNetNumberOfDesktops= XInternAtom(getDisplay(), XA_NET_NUMBER_OF_DESKTOPS, False);
979 _xaNetCurrentDesktop= XInternAtom(getDisplay(), XA_NET_CURRENT_DESKTOP, False);
980 _xaNetDesktopNames= XInternAtom(getDisplay(), XA_NET_DESKTOP_NAMES, False);
982 XGetWindowProperty(
983 getDisplay(), getRootWindow(), _xaNetNumberOfDesktops,
984 0, 8192, False, XA_CARDINAL, &xaType, &nFormat, &nItems, &nBytesAfter, &data
986 if ((nFormat == 32) && (nItems == 1) && (nBytesAfter == 0)) {
987 _nScreens= *(long*) data;
989 XFree(data);
991 XGetTextProperty(getDisplay(), getRootWindow(), &tp, _xaNetDesktopNames);
992 Xutf8TextPropertyToTextList(getDisplay(), &tp, &_szScreenNames, &_nDesktopNames);
993 XFree(tp.value);
995 _nLastScreen= -1;
996 setCurrentScreen(-1);
997 if (_nLastScreen == -1) {
998 fprintf(
999 stderr,
1000 "%s: couldn't determine current workspace.\n" \
1001 "Make sure your WindowMaker has EWMH support enabled!\n",
1002 getApplicationName()
1004 setCurrentScreen(0);
1006 if (isVerbose()) {
1007 int i;
1008 fprintf(stdout, "[%8ld] - %d worspaces found\n", currentTimeMillis(), getScreenCount());
1009 for (i= 0; i < getScreenCount(); i++) {
1010 fprintf(stdout, "[%8ld] - workspace %d: %s\n", currentTimeMillis(), i, getScreenName(i));
1012 fprintf(stdout, "[%8ld] - current workspace is %d (%s)\n", currentTimeMillis(),
1013 getCurrentScreen(), getScreenName(getCurrentScreen()));