wmmemfree: Add error handling for fscanf.
[dockapps.git] / wmpager / src / wmpager.c
blobc2c94a7e25d0b9f07aa74fd67e3d38ec8a0d8710
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 #ifndef WMPAGER_DEFAULT_INSTALL_DIR
38 #define WMPAGER_DEFAULT_INSTALL_DIR "/usr/local/share/wmpager/"
39 #endif
40 #define WMPAGER_USER_DIR ".wmpager/"
43 * Prototypes
46 void usage (int bVerbose);
47 void info ();
48 void setVerbose (int bVerbose);
49 int isVerbose ();
51 void initApplicationName (char* szApplicationName);
52 char* getApplicationName ();
54 void initDisplay (char* szDisplay);
55 void destroyDisplay ();
56 Display* getDisplay ();
58 Pixel getWhitePixel ();
59 Pixel getBlackPixel ();
60 int getDefaultScreen ();
61 int getDefaultDepth ();
62 void initWindow (int nArgc, char** szArgv);
63 void destroyWindow ();
64 GC getWindowGraphics ();
65 GC getMainGraphics ();
66 void initWindowMask (char* szInstallDir, char* szButtonTheme);
67 void redrawWindow ();
68 void getWindowOrigin (Window w, int* nX, int* nY);
70 void loop ();
72 void initButtons (int nButtons, int nColumns, int nRows);
73 int getButtonCount ();
74 int getButtonRowCount ();
75 int getButtonColumnCount ();
76 int getButtonWidth ();
77 int getButtonHeight ();
78 int getButtonAt (int nLocationX, int nLocationY);
79 void getButtonLocation (int nButton, int* nLocationX, int* nLocationY);
81 void initTime ();
82 long currentTimeMillis ();
84 void initScreens ();
85 int getScreenCount ();
86 char* getScreenName (int nScreen);
87 int getCurrentScreen ();
88 void setCurrentScreen (int nCurrentScreen);
89 void gotoScreen (int nWorkspace);
92 * Main
95 int main (int nArgc, char** szArgv) {
96 char* szDisplay= NULL;
97 char* szTheme= NULL;
98 char* szInstallDir= NULL;
99 int nWorkspaces= -1;
100 int bVerbose= 0;
101 int nSizeX= -1, nSizeY= -1;
102 int i;
103 initApplicationName(szArgv[0]);
104 /* we no longer use the WMPAGER environment variable
105 * szInstallDir= (char*) getenv(WMPAGER_ENV);
106 * instead we simply use a default installation directory
108 szInstallDir= WMPAGER_DEFAULT_INSTALL_DIR;
109 i= 1;
110 while (i < nArgc) {
111 if (strcmp("-h", szArgv[i]) == 0 || strcmp("--help", szArgv[i]) == 0) {
112 usage(1);
113 } else if (strcmp("-v", szArgv[i]) == 0 || strcmp("--verbose", szArgv[i]) == 0) {
114 bVerbose= 1;
115 } else if (strcmp("-w", szArgv[i]) == 0 || strcmp("--workspaces", szArgv[i]) == 0) {
116 i+= 1;
117 if (i < nArgc) {
118 sscanf(szArgv[i], "%d", &nWorkspaces);
119 if (nWorkspaces <= 0 || nWorkspaces > 9) {
120 fprintf(stderr, "%s: illegal number of workspaces '%s' for option '%s' (has to be 1-9)\n\n", getApplicationName(), szArgv[i], szArgv[i-1]);
121 usage(0);
123 } else {
124 fprintf(stderr, "%s: workspace count expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
125 usage(0);
127 } else if (strcmp("-s", szArgv[i]) == 0 || strcmp("--size", szArgv[i]) == 0) {
128 i+= 1;
129 if (i < nArgc) {
130 sscanf(szArgv[i], "%dx%d", &nSizeX, &nSizeY);
131 if (nSizeX <= 0 || nSizeX > 3 || nSizeY <= 0 || nSizeY > 3) {
132 fprintf(stderr, "%s: illegal size '%s' for option '%s' (has to be 1x1 .. 3x3)\n\n", getApplicationName(), szArgv[i], szArgv[i-1]);
133 usage(0);
135 } else {
136 fprintf(stderr, "%s: size argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
137 usage(0);
139 } else if (strcmp("-i", szArgv[i]) == 0 || strcmp("--installdir", szArgv[i]) == 0) {
140 i+= 1;
141 if (i < nArgc) {
142 struct stat buf;
143 szInstallDir= szArgv[i];
144 if (stat(szInstallDir, &buf) != 0) {
145 fprintf(stderr, "%s: cannot access installation directory '%s'\n\n", getApplicationName(), szArgv[i]);
146 usage(0);
148 } else {
149 fprintf(stderr, "%s: display argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
150 usage(0);
152 } else if (strcmp("-d", szArgv[i]) == 0 || strcmp("--display", szArgv[i]) == 0) {
153 i+= 1;
154 if (i < nArgc) {
155 szDisplay= szArgv[i];
156 } else {
157 fprintf(stderr, "%s: display argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
158 usage(0);
160 } else if (strcmp("-t", szArgv[i]) == 0 || strcmp("--theme", szArgv[i]) == 0) {
161 i+= 1;
162 if (i < nArgc) {
163 szTheme= strdup(szArgv[i]);
164 } else {
165 fprintf(stderr, "%s: theme argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
166 usage(0);
168 } else {
169 fprintf(stderr, "%s: unknown option '%s'\n\n", getApplicationName(), szArgv[i]);
170 usage(0);
172 i+= 1;
174 setVerbose(bVerbose);
175 if (isVerbose()) {
176 char* szRealDisplay= (szDisplay == NULL) ? (char*) getenv("DISPLAY") : szDisplay;
177 if (szRealDisplay == NULL) {
178 szRealDisplay= "localhost:0.0";
180 info();
181 fprintf(
182 stdout,
183 "[ ] startup options:\n" \
184 "[ ] - verbose= true\n" \
185 "[ ] - display= '%s'\n" \
186 "[ ] - installdir= '%s'\n" \
187 "[ ] - theme= '%s'\n" \
188 "[ ] - workspaces= '%d'\n" \
189 "[ ] - size= '%dx%d'\n",
190 szRealDisplay,
191 szInstallDir == NULL ? "<undefined>" : szInstallDir,
192 szTheme == NULL ? "<built-in>" : szTheme,
193 nWorkspaces,
194 nSizeX, nSizeY
197 initTime();
198 initDisplay(szDisplay);
199 initWindow(nArgc, szArgv);
200 initScreens();
201 initButtons(nWorkspaces, nSizeX, nSizeY);
202 initWindowMask(szInstallDir, szTheme);
203 loop();
204 return 0;
208 * Verbose
211 static int _bVerbose;
213 void setVerbose (int bVerbose) {
214 _bVerbose= bVerbose;
217 int isVerbose () {
218 return _bVerbose;
222 * Usage
225 #define USAGE \
226 "usage: %s [options]\n\n" \
227 "where options include:\n" \
228 " -h --help display usage and version information\n" \
229 " -v --verbose verbose message output\n" \
230 " -d --display <name> the display to use (defaults to the\n" \
231 " 'DISPLAY' environment variable)\n" \
232 " -s --size <w>x<h> number of buttons (default depends on the\n" \
233 " number of workspaces you have, i.e. 2x2 for 4\n" \
234 " workspaces, 2x3 for 6, maximum is 3x3)\n" \
235 " -w --workspaces <count> number of workspace buttons to display\n" \
236 " (default is the number of workspaces you have,\n" \
237 " maximum is 9)\n" \
238 " -t --theme <theme.xpm> the button theme to use, extension\n" \
239 " '.xpm' is optional, for more information about\n" \
240 " themes see docu (default is the built-in theme)\n" \
241 " -i --installdir <dir> specifies the installation directory location,\n" \
242 " this location is automatically searched for themes\n" \
243 " (defaults to the '/usr/local/share/wmpager/'\n" \
244 " and the user specific '~/.wmpager' directory)\n"
246 void usage (int bVerbose) {
247 if (bVerbose) {
248 info();
249 fprintf(stdout, USAGE, getApplicationName());
250 exit(0);
251 } else {
252 fprintf(stderr, USAGE, getApplicationName());
253 exit(-1);
257 void info () {
258 fprintf(stdout, "%s %s\n\n", APPLICATION, VERSION);
262 * Application
265 static char* _szApplicationName;
267 char* getApplicationName () {
268 return _szApplicationName;
271 void initApplicationName (char* szApplicationName) {
272 if (szApplicationName == NULL) {
273 _szApplicationName= APPLICATION;
274 } else {
275 _szApplicationName= strdup(szApplicationName);
280 * Display
283 static Display* _display;
284 static int _xfd;
286 Display* getDisplay () {
287 return _display;
290 void destroyDisplay () {
291 XCloseDisplay(getDisplay());
294 void initDisplay (char* szDisplay) {
295 if (szDisplay == NULL && ((char*) getenv("DISPLAY")) == NULL) {
296 szDisplay= ":0.0";
298 if (isVerbose()) {
299 char* szRealDisplay= (szDisplay == NULL) ? (char*) getenv("DISPLAY") : szDisplay;
300 if (szRealDisplay == NULL) {
301 szRealDisplay= "localhost:0.0";
303 fprintf(stdout, "[%8ld] initializing display '%s'\n", currentTimeMillis(), szRealDisplay);
305 _display= XOpenDisplay(szDisplay);
306 if (_display == NULL) {
307 fprintf(
308 stderr,
309 "%s: couldn't open display '%s'.\n",
310 getApplicationName(),
311 (szDisplay == NULL) ? ((char*) getenv("DISPLAY")) : szDisplay
313 exit(-1);
315 _xfd= XConnectionNumber(_display);
319 * Window
322 static int _nDefaultScreen, _nDefaultDepth;
323 static Window _wRoot, _wMain, _wIcon;
324 static GC _gcMain, _gcWindow;
325 static XpmAttributes _attrButtonTheme;
326 static Pixmap _pButtonTheme, _pButtonThemeMask;
327 static XpmAttributes _attrWindow;
328 static Pixmap _pWindow, _pWindowMask;
329 static Pixel _pWhite, _pBlack;
331 Pixel getWhitePixel () {
332 return _pWhite;
335 Pixel getBlackPixel () {
336 return _pBlack;
339 int getDefaultScreen () {
340 return _nDefaultScreen;
343 int getDefaultDepth () {
344 return _nDefaultDepth;
347 Window getRootWindow () {
348 return _wRoot;
351 Window getMainWindow () {
352 return _wMain;
355 Window getIconWindow () {
356 return _wIcon;
359 GC getMainGraphics () {
360 return _gcMain;
363 GC getWindowGraphics () {
364 return _gcWindow;
367 void initWindow (int nArgc, char** szArgv) {
368 char* szApplicationName= getApplicationName();
369 Display* display= getDisplay();
370 XSizeHints *xsizehints;
371 XWMHints* xwmhints;
372 XClassHint* xclasshint;
373 XTextProperty xtApplication;
375 if (isVerbose()) {
376 fprintf(stdout, "[%8ld] initializing application window\n", currentTimeMillis());
379 _nDefaultScreen= DefaultScreen(display);
380 _nDefaultDepth= DefaultDepth(display, _nDefaultScreen);
381 _wRoot= RootWindow(display, _nDefaultScreen);
383 XSelectInput(display, _wRoot, PropertyChangeMask);
385 _pWhite= WhitePixel(display, _nDefaultScreen);
386 _pBlack= BlackPixel(display, _nDefaultScreen);
388 xsizehints= XAllocSizeHints();
389 xsizehints->flags= USSize | USPosition;
390 xsizehints->width= xsizehints->height= 64;
392 _wMain= XCreateSimpleWindow(display, _wRoot, 0, 0, 64, 64, 5, _pWhite, _pBlack);
393 if (_wMain == 0) {
394 fprintf(stderr, "Cannot create main window.\n");
395 exit(-1);
398 _wIcon= XCreateSimpleWindow(display, _wMain, 0, 0, 64, 64, 5, _pWhite, _pBlack);
399 if (_wIcon == 0) {
400 fprintf(stderr, "Cannot create icon window.\n");
401 exit(-1);
404 xwmhints= XAllocWMHints();
405 xwmhints->flags= WindowGroupHint | IconWindowHint | StateHint;
406 xwmhints->icon_window= _wIcon;
407 xwmhints->window_group= _wMain;
408 xwmhints->initial_state= WithdrawnState;
409 XSetWMHints(display, _wMain, xwmhints);
411 xclasshint= XAllocClassHint();
412 xclasshint->res_name= APPLICATION;
413 xclasshint->res_class= APPLICATION;
414 XSetClassHint(display, _wMain, xclasshint);
416 XSetWMNormalHints(display, _wMain, xsizehints);
418 XFree(xclasshint);
419 XFree(xwmhints);
420 XFree(xsizehints);
422 if (XStringListToTextProperty(&szApplicationName, 1, &xtApplication) == 0) {
423 fprintf(stderr, "Cannot set window title.\n");
424 exit(-1);
426 XSetWMName(display, _wMain, &xtApplication);
427 XFree(xtApplication.value);
429 _gcMain= XCreateGC(display, _wMain, 0L, NULL);
430 if (_gcMain == NULL) {
431 fprintf(stderr, "Cannot create graphics context.\n");
432 exit(-1);
435 XSelectInput(display, _wMain, ExposureMask | ButtonPressMask | PointerMotionMask | StructureNotifyMask | LeaveWindowMask);
436 XSelectInput(display, _wIcon, ExposureMask | ButtonPressMask | PointerMotionMask | StructureNotifyMask | LeaveWindowMask);
438 XSetCommand(display, _wMain, szArgv, nArgc);
440 XMapWindow(display, _wMain);
443 void destroyWindow () {
444 XFreeGC(getDisplay(), getWindowGraphics());
445 XFreeGC(getDisplay(), getMainGraphics());
446 XDestroyWindow(getDisplay(), getMainWindow());
447 XDestroyWindow(getDisplay(), getIconWindow());
450 void initWindowMask (char* szInstallDir, char* szButtonTheme) {
451 Display* display= getDisplay();
452 GC gc;
453 Window wRoot= getRootWindow();
454 Window wMain= getMainWindow();
455 Window wIcon= getIconWindow();
456 Pixmap pOpaque, pTransparent, pMask;
457 char* mask= (char*) malloc(512);
458 int i;
460 if (isVerbose()) {
461 fprintf(stdout, "[%8ld] initializing window mask\n", currentTimeMillis());
463 for (i= 0; i < 512; i++) {
464 mask[i]= 0x00;
466 pTransparent= XCreateBitmapFromData(display, wRoot, mask, 64, 64);
467 if (pTransparent == 0) {
468 fprintf(stderr, "%s: couldn't create window mask (transparent).\n", getApplicationName());
469 exit(-1);
471 pMask= XCreateBitmapFromData(display, wRoot, mask, 64, 64);
472 if (pMask == 0) {
473 fprintf(stderr, "%s: couldn't create window mask (mask buffer).\n", getApplicationName());
474 exit(-1);
477 for (i= 0; i < 512; i++) {
478 mask[i]= 0xff;
480 pOpaque= XCreateBitmapFromData(display, wRoot, mask, 64, 64);
481 if (pOpaque == 0) {
482 fprintf(stderr, "%s: couldn't create window mask (opaque).\n", getApplicationName());
483 exit(-1);
486 gc= XCreateGC(display, pMask, 0L, NULL);
487 if (gc == NULL) {
488 fprintf(stderr, "%s: couldn't create window mask (mask graphics).\n", getApplicationName());
489 exit(-1);
491 for (i= 0; i < getButtonCount(); i++) {
492 int nButtonX, nButtonY;
493 getButtonLocation(i, &nButtonX, &nButtonY);
494 XCopyArea(display, pOpaque, pMask, gc, nButtonX, nButtonY, getButtonWidth(), getButtonHeight(), nButtonX, nButtonY);
497 free(mask);
498 XFreePixmap(display, pOpaque);
499 XFreePixmap(display, pTransparent);
500 XFreeGC(display, gc);
502 XShapeCombineMask(display, wMain, ShapeBounding, 0, 0, pMask, ShapeSet);
503 XShapeCombineMask(display, wIcon, ShapeBounding, 0, 0, pMask, ShapeSet);
505 if (isVerbose()) {
506 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(),
507 szButtonTheme == NULL ? "<built-in>" : szButtonTheme);
510 _attrButtonTheme.valuemask|= (XpmReturnPixels | XpmReturnExtensions);
511 if (szButtonTheme == NULL) {
512 if (
513 XpmCreatePixmapFromData(
514 display, wRoot, buttons_xpm, &_pButtonTheme, &_pButtonThemeMask, &_attrButtonTheme
515 ) != XpmSuccess
517 fprintf(stderr, "%s: couldn't create button theme.\n", getApplicationName());
518 exit(-1);
520 } else {
521 int bCheckAgain= 0;
522 struct stat buf;
523 /* check for absolute button theme pathname */
524 if (stat(szButtonTheme, &buf) == -1) {
525 char* szNewTheme= (char*) malloc(strlen(szButtonTheme) + 5);
526 strcpy(szNewTheme, szButtonTheme);
527 strcat(szNewTheme, ".xpm");
528 if (isVerbose()) {
529 fprintf(stdout, "[%8ld] theme file '%s' not found, trying '%s'\n", currentTimeMillis(), szButtonTheme, szNewTheme);
531 /* check for absolute button theme pathname (with .xpm added) */
532 if (stat(szNewTheme, &buf) == 0) {
533 free(szButtonTheme);
534 szButtonTheme= szNewTheme;
535 if (isVerbose()) {
536 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme);
538 } else {
539 bCheckAgain= 1;
540 free(szNewTheme);
543 if (bCheckAgain && szInstallDir != NULL) {
544 char* szNewTheme= (char*) malloc(strlen(szInstallDir) + strlen(szButtonTheme) + 6);
545 strcpy(szNewTheme, szInstallDir);
546 if (szNewTheme[strlen(szNewTheme) - 1] != '/') {
547 strcat(szNewTheme, "/");
549 strcat(szNewTheme, szButtonTheme);
550 if (stat(szNewTheme, &buf) == 0) {
551 bCheckAgain= 0;
552 free(szButtonTheme);
553 szButtonTheme= szNewTheme;
554 if (isVerbose()) {
555 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme);
557 } else {
558 strcat(szNewTheme, ".xpm");
559 if (stat(szNewTheme, &buf) == 0) {
560 bCheckAgain= 0;
561 free(szButtonTheme);
562 szButtonTheme= szNewTheme;
563 if (isVerbose()) {
564 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme);
566 } else {
567 free(szNewTheme);
571 if (bCheckAgain) {
572 /* as a goody check the ~/.wmpager directory if it exists */
573 char* szHome= (char*) getenv("HOME");
574 if (szHome) {
575 /* one really shouldn't copy&paste but hey this is a q&d tool */
576 char* szNewTheme= (char*) malloc(strlen(szHome) + strlen(szButtonTheme) + strlen(WMPAGER_USER_DIR) + 6);
577 strcpy(szNewTheme, szHome);
578 if (szNewTheme[strlen(szNewTheme) - 1] != '/') {
579 strcat(szNewTheme, "/");
581 strcat(szNewTheme, WMPAGER_USER_DIR);
582 strcat(szNewTheme, szButtonTheme);
583 if (stat(szNewTheme, &buf) == 0) {
584 bCheckAgain= 0;
585 free(szButtonTheme);
586 szButtonTheme= szNewTheme;
587 if (isVerbose()) {
588 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme);
590 } else {
591 strcat(szNewTheme, ".xpm");
592 if (stat(szNewTheme, &buf) == 0) {
593 bCheckAgain= 0;
594 free(szButtonTheme);
595 szButtonTheme= szNewTheme;
596 if (isVerbose()) {
597 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme);
599 } else {
600 free(szNewTheme);
605 if (
606 XpmReadFileToPixmap(
607 display, wRoot, szButtonTheme, &_pButtonTheme, &_pButtonThemeMask, &_attrButtonTheme
608 ) != XpmSuccess
610 fprintf(stderr, "%s: couldn't read button theme '%s'.\n", getApplicationName(), szButtonTheme);
611 exit(-1);
614 free(szButtonTheme);
617 if (isVerbose()) {
618 fprintf(stdout, "[%8ld] initializing screen buffer\n", currentTimeMillis());
621 _attrWindow.valuemask|= (XpmReturnPixels | XpmReturnExtensions);
622 if (
623 XpmCreatePixmapFromData(
624 display, wRoot, screen_xpm, &_pWindow, &_pWindowMask, &_attrWindow
625 ) != XpmSuccess
627 fprintf(stderr, "%s: couldn't create screen buffer.\n", getApplicationName());
628 exit(-1);
631 _gcWindow= XCreateGC(_display, _pWindow, 0L, NULL);
632 if (_gcWindow == NULL) {
633 fprintf(stderr, "%s: couldn't create screen buffer graphics.\n", getApplicationName());
634 exit(-1);
638 void redrawWindow () {
639 XEvent event;
640 int i;
641 int w= getButtonWidth();
642 int h= getButtonHeight();
643 for (i= 0; i < getButtonCount(); i++) {
644 int x, y, xoff, yoff;
645 xoff= 51;
646 yoff= 10;
647 if (i == getCurrentScreen()) {
648 xoff= 0;
649 yoff= 0;
651 getButtonLocation(i, &x, &y);
652 XSetClipMask(_display, _gcWindow, _pWindowMask);
653 XSetClipOrigin(_display, _gcWindow, 0, 0);
654 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff + x - 7, y - 7, w, h, x, y);
655 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff, 0, w, 1, x, y);
656 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff, 0, 1, h, x, y);
657 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff + 50, 0, 1, h, x + w - 1, y);
658 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff, 50, w, 1, x, y + h - 1);
659 XSetClipMask(_display, _gcWindow, _pButtonThemeMask);
660 XSetClipOrigin(_display, _gcWindow, (x + (w - 10) / 2) - i * 10, -51 - yoff + y + (h - 10) / 2);
661 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, i * 10, 51 + yoff, 10, 10, x + (w - 10) / 2, y + (h - 10) / 2);
663 while (XCheckTypedWindowEvent(_display, _wMain, Expose, &event));
664 XCopyArea(_display, _pWindow, _wMain, _gcMain, 0, 0, 64, 64, 0, 0);
665 while (XCheckTypedWindowEvent(_display, _wIcon, Expose, &event));
666 XCopyArea(_display, _pWindow, _wIcon, _gcMain, 0, 0, 64, 64, 0, 0);
669 void getWindowOrigin (Window w, int* nX, int* nY) {
670 Window wWindow, wParent, wRoot;
671 Window* wChildren;
672 unsigned int nChildren;
673 unsigned int ww, wh, wb, wd;
674 int wx, wy;
676 wParent= w;
677 do {
678 wWindow= wParent;
679 if (!XQueryTree(getDisplay(), wParent, &wRoot, &wParent, &wChildren, &nChildren)) {
680 return;
682 if (wChildren) {
683 XFree(wChildren);
685 } while (wParent != wRoot);
687 if (XGetGeometry(getDisplay(), wWindow, &wRoot, &wx, &wy, &ww, &wh, &wb, &wd)) {
688 if (nX) {
689 *nX= wx;
691 if (nY) {
692 *nY= wy;
698 * Event Loop
701 void loop () {
702 Display* display= getDisplay();
703 XEvent event;
704 char* atom_name;
705 struct timeval tv;
706 fd_set fds;
708 if (isVerbose()) {
709 fprintf(stdout, "[%8ld] starting event loop\n", currentTimeMillis());
711 for (;;) {
712 while (XPending(display)) {
713 XNextEvent(display, &event);
714 switch (event.type) {
715 case Expose:
716 if (event.xexpose.count == 0) {
717 redrawWindow();
719 break;
720 case ConfigureNotify:
721 redrawWindow();
722 break;
723 case ButtonPress:
725 int nButton= getButtonAt(event.xbutton.x, event.xbutton.y);
726 if (isVerbose()) {
727 fprintf(stdout, "[%8ld] button %d pressed\n", currentTimeMillis(), nButton);
729 if (nButton != -1) {
730 setCurrentScreen(nButton);
731 gotoScreen(nButton);
734 break;
735 case PropertyNotify:
736 atom_name = XGetAtomName(getDisplay(), event.xproperty.atom);
737 if (atom_name == NULL)
738 break;
739 if (strcmp(XA_NET_CURRENT_DESKTOP, atom_name) == 0) {
740 setCurrentScreen(-1);
741 if (isVerbose()) {
742 fprintf(stdout, "[%8ld] new current workspace (%d= %s)\n",
743 currentTimeMillis(), getCurrentScreen(), getScreenName(getCurrentScreen()));
745 redrawWindow();
747 XFree(atom_name);
748 break;
749 case DestroyNotify:
750 if (isVerbose()) {
751 fprintf(stdout, "[%8ld] quit application\n", currentTimeMillis());
753 destroyWindow();
754 destroyDisplay();
755 exit(0);
756 break;
760 tv.tv_sec = 0;
761 tv.tv_usec = 500000UL;
762 FD_ZERO(&fds);
763 FD_SET(_xfd, &fds);
764 select(_xfd + 1, &fds, NULL, NULL, &tv);
769 * Button Helpers
772 static int _nButtons;
773 static int _nButtonRows, _nButtonColumns;
774 static int _nButtonWidth, _nButtonHeight;
776 int getButtonCount () {
777 return _nButtons;
780 int getButtonRowCount () {
781 return _nButtonRows;
784 int getButtonColumnCount () {
785 return _nButtonColumns;
788 int getButtonWidth () {
789 return _nButtonWidth;
792 int getButtonHeight () {
793 return _nButtonHeight;
796 int getButtonAt (int nLocationX, int nLocationY) {
797 int i, nButtonX, nButtonY;
798 for (i= 0; i < _nButtons; i++) {
799 getButtonLocation(i, &nButtonX, &nButtonY);
800 if (
801 nLocationX >= nButtonX && nLocationX < nButtonX + _nButtonWidth &&
802 nLocationY >= nButtonY && nLocationY < nButtonY + _nButtonHeight
804 return i;
807 return -1;
810 void getButtonLocation (int nButton, int* nLocationX, int* nLocationY) {
811 if (nButton < 0 || nButton > _nButtons) {
812 *nLocationX= *nLocationY= 0;
813 } else {
814 *nLocationX= *nLocationY= 7;
815 while (nButton >= _nButtonColumns) {
816 *nLocationY+= _nButtonHeight + 3;
817 nButton-= _nButtonColumns;
819 while (nButton > 0) {
820 *nLocationX+= _nButtonWidth + 3;
821 nButton--;
826 void initButtons (int nButtons, int nColumns, int nRows) {
827 if (nButtons != -1) {
828 _nButtons= nButtons;
829 } else {
830 _nButtons= getScreenCount();
831 if (_nButtons > 9) {
832 /* only handle nine screens at most */
833 _nButtons= 9;
836 if (nColumns == -1 || nRows == -1) {
837 switch (_nButtons) {
838 case 1:
839 _nButtonRows= _nButtonColumns= 1;
840 break;
841 case 2:
842 _nButtonColumns= 1;
843 _nButtonRows= 2;
844 break;
845 case 3:
846 /* fallthrough */
847 case 4:
848 _nButtonColumns= _nButtonRows= 2;
849 break;
850 case 5:
851 /* fallthrough */
852 case 6:
853 _nButtonColumns= 2;
854 _nButtonRows= 3;
855 break;
856 default:
857 _nButtonColumns= _nButtonRows= 3;
858 break;
860 } else {
861 _nButtonColumns= nColumns;
862 _nButtonRows= nRows;
864 if (_nButtons > _nButtonColumns * _nButtonRows) {
865 _nButtons= _nButtonColumns * _nButtonRows;
867 if (isVerbose()) {
868 fprintf(stdout, "[%8ld] initializing buttons\n", currentTimeMillis());
869 fprintf(stdout, "[%8ld] - %d workspace buttons\n", currentTimeMillis(), _nButtons);
870 fprintf(stdout, "[%8ld] - button layout %dx%d\n", currentTimeMillis(), _nButtonColumns, _nButtonRows);
873 if (_nButtonColumns == 1) {
874 _nButtonWidth= 51;
875 } else if (_nButtonColumns == 2) {
876 _nButtonWidth= 24;
877 } else {
878 _nButtonWidth= 15;
881 if (_nButtonRows == 1) {
882 _nButtonHeight= 51;
883 } else if (_nButtonRows == 2) {
884 _nButtonHeight= 24;
885 } else {
886 _nButtonHeight= 15;
892 * Time
895 static struct timeval _tStart;
897 void initTime () {
898 if (isVerbose()) {
899 fprintf(stdout, "[ ] initializing time\n");
901 gettimeofday(&_tStart, NULL);
904 long currentTimeMillis () {
905 struct timeval tNow;
906 struct timeval tElapsed;
908 gettimeofday(&tNow, NULL);
910 if (_tStart.tv_usec > tNow.tv_usec) {
911 tNow.tv_usec+= 1000000;
912 tNow.tv_sec--;
914 tElapsed.tv_sec= tNow.tv_sec - _tStart.tv_sec;
915 tElapsed.tv_usec= tNow.tv_usec - _tStart.tv_usec;
916 return (tElapsed.tv_sec * 1000) + (tElapsed.tv_usec / 1000);
920 * Screen Handling
923 static Atom _xaNetNumberOfDesktops;
924 static Atom _xaNetCurrentDesktop;
925 static Atom _xaNetDesktopNames;
926 static int _nScreens;
927 static char** _szScreenNames;
928 static int _nDesktopNames;
929 static int _nLastScreen;
931 int getScreenCount () {
932 return _nScreens;
935 char* getScreenName (int nScreen) {
936 if (nScreen < 0 || nScreen >= _nDesktopNames) {
937 return "<empty>";
939 return _szScreenNames[nScreen];
942 void gotoScreen (int nWorkspace) {
943 XEvent event;
944 event.type= ClientMessage;
945 event.xclient.type= ClientMessage;
946 event.xclient.window= getRootWindow();
947 event.xclient.message_type= _xaNetCurrentDesktop;
948 event.xclient.format= 32;
949 event.xclient.data.l[0]= nWorkspace;
950 event.xclient.data.l[1]= currentTimeMillis();
951 XSendEvent(getDisplay(), getRootWindow(), False, SubstructureNotifyMask, (XEvent*) &event);
954 int getCurrentScreen () {
955 return _nLastScreen;
958 void setCurrentScreen (int nCurrentScreen) {
959 if (nCurrentScreen == -1) {
960 long nScreen= _nLastScreen;
961 Atom xaType;
962 int nFormat;
963 unsigned long nItems, nBytesAfter;
964 unsigned char* data;
966 XGetWindowProperty(
967 getDisplay(), getRootWindow(), _xaNetCurrentDesktop,
968 0, 8192, False, XA_CARDINAL, &xaType, &nFormat, &nItems, &nBytesAfter, &data
970 if ((nFormat == 32) && (nItems == 1) && (nBytesAfter == 0)) {
971 nScreen= *(long*) data;
973 XFree(data);
974 _nLastScreen= nScreen;
975 } else {
976 _nLastScreen= nCurrentScreen;
980 void initScreens () {
981 XTextProperty tp;
982 Atom xaType;
983 int nFormat;
984 unsigned long nItems, nBytesAfter;
985 unsigned char* data;
987 if (isVerbose()) {
988 fprintf(stdout, "[%8ld] initializing window maker communication\n", currentTimeMillis());
990 _xaNetNumberOfDesktops= XInternAtom(getDisplay(), XA_NET_NUMBER_OF_DESKTOPS, False);
991 _xaNetCurrentDesktop= XInternAtom(getDisplay(), XA_NET_CURRENT_DESKTOP, False);
992 _xaNetDesktopNames= XInternAtom(getDisplay(), XA_NET_DESKTOP_NAMES, False);
994 XGetWindowProperty(
995 getDisplay(), getRootWindow(), _xaNetNumberOfDesktops,
996 0, 8192, False, XA_CARDINAL, &xaType, &nFormat, &nItems, &nBytesAfter, &data
998 if ((nFormat == 32) && (nItems == 1) && (nBytesAfter == 0)) {
999 _nScreens= *(long*) data;
1001 XFree(data);
1003 XGetTextProperty(getDisplay(), getRootWindow(), &tp, _xaNetDesktopNames);
1004 Xutf8TextPropertyToTextList(getDisplay(), &tp, &_szScreenNames, &_nDesktopNames);
1005 XFree(tp.value);
1007 _nLastScreen= -1;
1008 setCurrentScreen(-1);
1009 if (_nLastScreen == -1) {
1010 fprintf(
1011 stderr,
1012 "%s: couldn't determine current workspace.\n" \
1013 "Make sure your WindowMaker has EWMH support enabled!\n",
1014 getApplicationName()
1016 setCurrentScreen(0);
1018 if (isVerbose()) {
1019 int i;
1020 fprintf(stdout, "[%8ld] - %d worspaces found\n", currentTimeMillis(), getScreenCount());
1021 for (i= 0; i < getScreenCount(); i++) {
1022 fprintf(stdout, "[%8ld] - workspace %d: %s\n", currentTimeMillis(), i, getScreenName(i));
1024 fprintf(stdout, "[%8ld] - current workspace is %d (%s)\n", currentTimeMillis(),
1025 getCurrentScreen(), getScreenName(getCurrentScreen()));