wmpager: EWMH support
[dockapps.git] / wmpager / src / wmpager.c
blobedf6c3d8011fcc9714c8b11e5884e5c8113aa01c
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"
30 #define BUILD_REV "$Revision: 1.4 $"
31 #define BUILD_DATE "$Date: 2002/08/16 17:22:26 $"
33 #define XA_NET_NUMBER_OF_DESKTOPS "_NET_NUMBER_OF_DESKTOPS"
34 #define XA_NET_CURRENT_DESKTOP "_NET_CURRENT_DESKTOP"
35 #define XA_NET_DESKTOP_NAMES "_NET_DESKTOP_NAMES"
37 #define WMPAGER_ENV "WMPAGER"
38 #define WMPAGER_DEFAULT_INSTALL_DIR "/usr/local/share/wmpager/"
39 #define WMPAGER_USER_DIR ".wmpager/"
42 * Prototypes
45 void usage (int bVerbose);
46 void info ();
47 void setVerbose (int bVerbose);
48 int isVerbose ();
50 void initApplicationName (char* szApplicationName);
51 char* getApplicationName ();
53 void initDisplay (char* szDisplay);
54 void destroyDisplay ();
55 Display* getDisplay ();
57 Pixel getWhitePixel ();
58 Pixel getBlackPixel ();
59 int getDefaultScreen ();
60 int getDefaultDepth ();
61 void initWindow (int nArgc, char** szArgv);
62 void destroyWindow ();
63 GC getWindowGraphics ();
64 GC getMainGraphics ();
65 void initWindowMask (char* szInstallDir, char* szButtonTheme);
66 void redrawWindow ();
67 void getWindowOrigin (Window w, int* nX, int* nY);
69 void loop ();
71 void initButtons (int nButtons, int nColumns, int nRows);
72 int getButtonCount ();
73 int getButtonRowCount ();
74 int getButtonColumnCount ();
75 int getButtonWidth ();
76 int getButtonHeight ();
77 int getButtonAt (int nLocationX, int nLocationY);
78 void getButtonLocation (int nButton, int* nLocationX, int* nLocationY);
80 void initTime ();
81 long currentTimeMillis ();
83 void initScreens ();
84 int getScreenCount ();
85 char* getScreenName (int nScreen);
86 int getCurrentScreen ();
87 void setCurrentScreen (int nCurrentScreen);
88 void gotoScreen (int nWorkspace);
91 * Main
94 int main (int nArgc, char** szArgv) {
95 char* szDisplay= NULL;
96 char* szTheme= NULL;
97 char* szInstallDir= NULL;
98 int nWorkspaces= -1;
99 int bVerbose= 0;
100 int nSizeX= -1, nSizeY= -1;
101 int i;
102 initApplicationName(szArgv[0]);
103 /* we no longer use the WMPAGER environment variable
104 * szInstallDir= (char*) getenv(WMPAGER_ENV);
105 * instead we simply use a default installation directory
107 szInstallDir= WMPAGER_DEFAULT_INSTALL_DIR;
108 i= 1;
109 while (i < nArgc) {
110 if (strcmp("-h", szArgv[i]) == 0 || strcmp("--help", szArgv[i]) == 0) {
111 usage(1);
112 } else if (strcmp("-v", szArgv[i]) == 0 || strcmp("--verbose", szArgv[i]) == 0) {
113 bVerbose= 1;
114 } else if (strcmp("-w", szArgv[i]) == 0 || strcmp("--workspaces", szArgv[i]) == 0) {
115 i+= 1;
116 if (i < nArgc) {
117 sscanf(szArgv[i], "%d", &nWorkspaces);
118 if (nWorkspaces <= 0 || nWorkspaces > 9) {
119 fprintf(stderr, "%s: illegal number of workspaces '%s' for option '%s' (has to be 1-9)\n\n", getApplicationName(), szArgv[i], szArgv[i-1]);
120 usage(0);
122 } else {
123 fprintf(stderr, "%s: workspace count expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
124 usage(0);
126 } else if (strcmp("-s", szArgv[i]) == 0 || strcmp("--size", szArgv[i]) == 0) {
127 i+= 1;
128 if (i < nArgc) {
129 sscanf(szArgv[i], "%dx%d", &nSizeX, &nSizeY);
130 if (nSizeX <= 0 || nSizeX > 3 || nSizeY <= 0 || nSizeY > 3) {
131 fprintf(stderr, "%s: illegal size '%s' for option '%s' (has to be 1x1 .. 3x3)\n\n", getApplicationName(), szArgv[i], szArgv[i-1]);
132 usage(0);
134 } else {
135 fprintf(stderr, "%s: size argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
136 usage(0);
138 } else if (strcmp("-i", szArgv[i]) == 0 || strcmp("--installdir", szArgv[i]) == 0) {
139 i+= 1;
140 if (i < nArgc) {
141 struct stat buf;
142 szInstallDir= szArgv[i];
143 if (stat(szInstallDir, &buf) != 0) {
144 fprintf(stderr, "%s: cannot access installation directory '%s'\n\n", getApplicationName(), szArgv[i]);
145 usage(0);
147 } else {
148 fprintf(stderr, "%s: display argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
149 usage(0);
151 } else if (strcmp("-d", szArgv[i]) == 0 || strcmp("--display", szArgv[i]) == 0) {
152 i+= 1;
153 if (i < nArgc) {
154 szDisplay= szArgv[i];
155 } else {
156 fprintf(stderr, "%s: display argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
157 usage(0);
159 } else if (strcmp("-t", szArgv[i]) == 0 || strcmp("--theme", szArgv[i]) == 0) {
160 i+= 1;
161 if (i < nArgc) {
162 szTheme= szArgv[i];
163 } else {
164 fprintf(stderr, "%s: theme argument expected for '%s'\n\n", getApplicationName(), szArgv[i-1]);
165 usage(0);
167 } else {
168 fprintf(stderr, "%s: unknown option '%s'\n\n", getApplicationName(), szArgv[i]);
169 usage(0);
171 i+= 1;
173 setVerbose(bVerbose);
174 if (isVerbose()) {
175 char* szRealDisplay= (szDisplay == NULL) ? (char*) getenv("DISPLAY") : szDisplay;
176 if (szRealDisplay == NULL) {
177 szRealDisplay= "localhost:0.0";
179 info();
180 fprintf(
181 stdout,
182 "[ ] startup options:\n" \
183 "[ ] - verbose= true\n" \
184 "[ ] - display= '%s'\n" \
185 "[ ] - installdir= '%s'\n" \
186 "[ ] - theme= '%s'\n" \
187 "[ ] - workspaces= '%d'\n" \
188 "[ ] - size= '%dx%d'\n",
189 szRealDisplay,
190 szInstallDir == NULL ? "<undefined>" : szInstallDir,
191 szTheme == NULL ? "<built-in>" : szTheme,
192 nWorkspaces,
193 nSizeX, nSizeY
196 initTime();
197 initDisplay(szDisplay);
198 initWindow(nArgc, szArgv);
199 initScreens();
200 initButtons(nWorkspaces, nSizeX, nSizeY);
201 initWindowMask(szInstallDir, szTheme);
202 loop();
203 return 0;
207 * Verbose
210 static int _bVerbose;
212 void setVerbose (int bVerbose) {
213 _bVerbose= bVerbose;
216 int isVerbose () {
217 return _bVerbose;
221 * Usage
224 #define USAGE \
225 "usage: %s [options]\n\n" \
226 "where options include:\n" \
227 " -h --help display usage and version information\n" \
228 " -v --verbose verbose message output\n" \
229 " -d --display <name> the display to use (defaults to the\n" \
230 " 'DISPLAY' environment variable)\n" \
231 " -s --size <w>x<h> number of buttons (default depends on the\n" \
232 " number of workspaces you have, i.e. 2x2 for 4\n" \
233 " workspaces, 2x3 for 6, maximum is 3x3)\n" \
234 " -w --workspaces <count> number of workspace buttons to display\n" \
235 " (default is the number of workspaces you have,\n" \
236 " maximum is 9)\n" \
237 " -t --theme <theme.xpm> the button theme to use, extension\n" \
238 " '.xpm' is optional, for more information about\n" \
239 " themes see docu (default is the built-in theme)\n" \
240 " -i --installdir <dir> specifies the installation directory location,\n" \
241 " this location is automatically searched for themes\n" \
242 " (defaults to the '/usr/local/share/wmpager/'\n" \
243 " and the user specific '~/.wmpager' directory)\n"
245 void usage (int bVerbose) {
246 if (bVerbose) {
247 info();
248 fprintf(stdout, USAGE, getApplicationName());
249 exit(0);
250 } else {
251 fprintf(stderr, USAGE, getApplicationName());
252 exit(-1);
256 void info () {
257 char* szRev= strdup(BUILD_REV);
258 char* szDate= strdup(BUILD_DATE);
259 szRev= &szRev[11];
260 szRev[strlen(szRev) - 2]= '\0';
261 szDate= &szDate[7];
262 szDate[strlen(szDate) - 2]= '\0';
263 fprintf(stdout, "%s %s (build %s, %s)\n\n", APPLICATION, VERSION, szRev, szDate);
267 * Application
270 static char* _szApplicationName;
272 char* getApplicationName () {
273 return _szApplicationName;
276 void initApplicationName (char* szApplicationName) {
277 if (szApplicationName == NULL) {
278 _szApplicationName= APPLICATION;
279 } else {
280 _szApplicationName= strdup(szApplicationName);
285 * Display
288 static Display* _display;
290 Display* getDisplay () {
291 return _display;
294 void destroyDisplay () {
295 XCloseDisplay(getDisplay());
298 void initDisplay (char* szDisplay) {
299 if (szDisplay == NULL && ((char*) getenv("DISPLAY")) == NULL) {
300 szDisplay= ":0.0";
302 if (isVerbose()) {
303 char* szRealDisplay= (szDisplay == NULL) ? (char*) getenv("DISPLAY") : szDisplay;
304 if (szRealDisplay == NULL) {
305 szRealDisplay= "localhost:0.0";
307 fprintf(stdout, "[%8ld] initializing display '%s'\n", currentTimeMillis(), szRealDisplay);
309 _display= XOpenDisplay(szDisplay);
310 if (_display == NULL) {
311 fprintf(
312 stderr,
313 "%s: couldn't open display '%s'.\n",
314 getApplicationName(),
315 (szDisplay == NULL) ? ((char*) getenv("DISPLAY")) : szDisplay
317 exit(-1);
322 * Window
325 static int _nDefaultScreen, _nDefaultDepth;
326 static Window _wRoot, _wMain, _wIcon;
327 static GC _gcMain, _gcWindow;
328 static XpmAttributes _attrButtonTheme;
329 static Pixmap _pButtonTheme, _pButtonThemeMask;
330 static XpmAttributes _attrWindow;
331 static Pixmap _pWindow, _pWindowMask;
332 static Pixel _pWhite, _pBlack;
334 Pixel getWhitePixel () {
335 return _pWhite;
338 Pixel getBlackPixel () {
339 return _pBlack;
342 int getDefaultScreen () {
343 return _nDefaultScreen;
346 int getDefaultDepth () {
347 return _nDefaultDepth;
350 Window getRootWindow () {
351 return _wRoot;
354 Window getMainWindow () {
355 return _wMain;
358 Window getIconWindow () {
359 return _wIcon;
362 GC getMainGraphics () {
363 return _gcMain;
366 GC getWindowGraphics () {
367 return _gcWindow;
370 void initWindow (int nArgc, char** szArgv) {
371 char* szApplicationName= getApplicationName();
372 Display* display= getDisplay();
373 XSizeHints *xsizehints;
374 XWMHints* xwmhints;
375 XClassHint* xclasshint;
376 XTextProperty* xtApplication;
377 XGCValues xgcMain;
379 if (isVerbose()) {
380 fprintf(stdout, "[%8ld] initializing application window\n", currentTimeMillis());
383 _nDefaultScreen= DefaultScreen(display);
384 _nDefaultDepth= DefaultDepth(display, _nDefaultScreen);
385 _wRoot= RootWindow(display, _nDefaultScreen);
387 XSelectInput(display, _wRoot, PropertyChangeMask);
389 _pWhite= WhitePixel(display, _nDefaultScreen);
390 _pBlack= BlackPixel(display, _nDefaultScreen);
392 xsizehints= XAllocSizeHints();
393 xsizehints->flags= USSize | USPosition;
394 xsizehints->width= xsizehints->height= 64;
396 _wMain= XCreateSimpleWindow(display, _wRoot, 0, 0, 64, 64, 5, _pWhite, _pBlack);
397 if (_wMain == 0) {
398 fprintf(stderr, "Cannot create main window.\n");
399 exit(-1);
402 _wIcon= XCreateSimpleWindow(display, _wMain, 0, 0, 64, 64, 5, _pWhite, _pBlack);
403 if (_wIcon == 0) {
404 fprintf(stderr, "Cannot create icon window.\n");
405 exit(-1);
408 xwmhints= XAllocWMHints();
409 xwmhints->flags= WindowGroupHint | IconWindowHint | StateHint;
410 xwmhints->icon_window= _wIcon;
411 xwmhints->window_group= _wMain;
412 xwmhints->initial_state= WithdrawnState;
413 XSetWMHints(display, _wMain, xwmhints);
415 xclasshint= XAllocClassHint();
416 xclasshint->res_name= APPLICATION;
417 xclasshint->res_class= APPLICATION;
418 XSetClassHint(display, _wMain, xclasshint);
420 XSetWMNormalHints(display, _wMain, xsizehints);
422 xtApplication= (XTextProperty*) malloc(sizeof(XTextProperty));
423 if (XStringListToTextProperty(&szApplicationName, 1, xtApplication) == 0) {
424 fprintf(stderr, "Cannot set window title.\n");
425 exit(-1);
427 XSetWMName(display, _wMain, xtApplication);
429 _gcMain= XCreateGC(display, _wMain, (GCForeground | GCBackground), &xgcMain);
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 XGCValues xgc, xgcWindow;
457 Pixmap pOpaque, pTransparent, pMask;
458 char* mask= (char*) malloc(512);
459 int i;
461 if (isVerbose()) {
462 fprintf(stdout, "[%8ld] initializing window mask\n", currentTimeMillis());
464 for (i= 0; i < 512; i++) {
465 mask[i]= 0x00;
467 pTransparent= XCreateBitmapFromData(display, wRoot, mask, 64, 64);
468 if (pTransparent == 0) {
469 fprintf(stderr, "%s: couldn't create window mask (transparent).\n", getApplicationName());
470 exit(-1);
472 pMask= XCreateBitmapFromData(display, wRoot, mask, 64, 64);
473 if (pMask == 0) {
474 fprintf(stderr, "%s: couldn't create window mask (mask buffer).\n", getApplicationName());
475 exit(-1);
478 for (i= 0; i < 512; i++) {
479 mask[i]= 0xff;
481 pOpaque= XCreateBitmapFromData(display, wRoot, mask, 64, 64);
482 if (pOpaque == 0) {
483 fprintf(stderr, "%s: couldn't create window mask (opaque).\n", getApplicationName());
484 exit(-1);
487 gc= XCreateGC(display, pMask, (GCForeground | GCBackground), &xgc);
488 if (gc == NULL) {
489 fprintf(stderr, "%s: couldn't create window mask (mask graphics).\n", getApplicationName());
490 exit(-1);
492 for (i= 0; i < getButtonCount(); i++) {
493 int nButtonX, nButtonY;
494 getButtonLocation(i, &nButtonX, &nButtonY);
495 XCopyArea(display, pOpaque, pMask, gc, nButtonX, nButtonY, getButtonWidth(), getButtonHeight(), nButtonX, nButtonY);
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) + 4);
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 szButtonTheme= szNewTheme;
534 if (isVerbose()) {
535 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme);
537 } else {
538 bCheckAgain= 1;
539 free(szNewTheme);
542 if (bCheckAgain && szInstallDir != NULL) {
543 char* szNewTheme= (char*) malloc(strlen(szInstallDir) + strlen(szButtonTheme) + 5);
544 strcpy(szNewTheme, szInstallDir);
545 if (szNewTheme[strlen(szNewTheme) - 1] != '/') {
546 strcat(szNewTheme, "/");
548 strcat(szNewTheme, szButtonTheme);
549 if (stat(szNewTheme, &buf) == 0) {
550 bCheckAgain= 0;
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 szButtonTheme= szNewTheme;
560 if (isVerbose()) {
561 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme);
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) + 5);
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 szButtonTheme= szNewTheme;
581 if (isVerbose()) {
582 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme);
584 } else {
585 strcat(szNewTheme, ".xpm");
586 if (stat(szNewTheme, &buf) == 0) {
587 bCheckAgain= 0;
588 szButtonTheme= szNewTheme;
589 if (isVerbose()) {
590 fprintf(stdout, "[%8ld] initializing button theme '%s'\n", currentTimeMillis(), szButtonTheme);
596 if (
597 XpmReadFileToPixmap(
598 display, wRoot, szButtonTheme, &_pButtonTheme, &_pButtonThemeMask, &_attrButtonTheme
599 ) != XpmSuccess
601 fprintf(stderr, "%s: couldn't read button theme '%s'.\n", getApplicationName(), szButtonTheme);
602 exit(-1);
606 if (isVerbose()) {
607 fprintf(stdout, "[%8ld] initializing screen buffer\n", currentTimeMillis());
610 _attrWindow.valuemask|= (XpmReturnPixels | XpmReturnExtensions);
611 if (
612 XpmCreatePixmapFromData(
613 display, wRoot, screen_xpm, &_pWindow, &_pWindowMask, &_attrWindow
614 ) != XpmSuccess
616 fprintf(stderr, "%s: couldn't create screen buffer.\n", getApplicationName());
617 exit(-1);
620 _gcWindow= XCreateGC(_display, _pWindow, (GCForeground | GCBackground), &xgcWindow);
621 if (gc == NULL) {
622 fprintf(stderr, "%s: couldn't create screen buffer graphics.\n", getApplicationName());
623 exit(-1);
627 void redrawWindow () {
628 XEvent event;
629 int i;
630 int w= getButtonWidth();
631 int h= getButtonHeight();
632 for (i= 0; i < getButtonCount(); i++) {
633 int x, y, xoff, yoff;
634 xoff= 51;
635 yoff= 10;
636 if (i == getCurrentScreen()) {
637 xoff= 0;
638 yoff= 0;
640 getButtonLocation(i, &x, &y);
641 XSetClipMask(_display, _gcWindow, _pWindowMask);
642 XSetClipOrigin(_display, _gcWindow, 0, 0);
643 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff + x - 7, y - 7, w, h, x, y);
644 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff, 0, w, 1, x, y);
645 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff, 0, 1, h, x, y);
646 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff + 50, 0, 1, h, x + w - 1, y);
647 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, xoff, 50, w, 1, x, y + h - 1);
648 XSetClipMask(_display, _gcWindow, _pButtonThemeMask);
649 XSetClipOrigin(_display, _gcWindow, (x + (w - 10) / 2) - i * 10, -51 - yoff + y + (h - 10) / 2);
650 XCopyArea(_display, _pButtonTheme, _pWindow, _gcWindow, i * 10, 51 + yoff, 10, 10, x + (w - 10) / 2, y + (h - 10) / 2);
652 while (XCheckTypedWindowEvent(_display, _wMain, Expose, &event));
653 XCopyArea(_display, _pWindow, _wMain, _gcMain, 0, 0, 64, 64, 0, 0);
654 while (XCheckTypedWindowEvent(_display, _wIcon, Expose, &event));
655 XCopyArea(_display, _pWindow, _wIcon, _gcMain, 0, 0, 64, 64, 0, 0);
658 void getWindowOrigin (Window w, int* nX, int* nY) {
659 Window wWindow, wParent, wRoot;
660 Window* wChildren;
661 unsigned int nChildren;
662 unsigned int ww, wh, wb, wd;
663 int wx, wy;
665 wParent= w;
666 do {
667 wWindow= wParent;
668 if (!XQueryTree(getDisplay(), wParent, &wRoot, &wParent, &wChildren, &nChildren)) {
669 return;
671 if (wChildren) {
672 XFree(wChildren);
674 } while (wParent != wRoot);
676 if (XGetGeometry(getDisplay(), wWindow, &wRoot, &wx, &wy, &ww, &wh, &wb, &wd)) {
677 if (nX) {
678 *nX= wx;
680 if (nY) {
681 *nY= wy;
687 * Event Loop
690 void loop () {
691 Display* display= getDisplay();
692 XEvent event;
694 if (isVerbose()) {
695 fprintf(stdout, "[%8ld] starting event loop\n", currentTimeMillis());
697 for (;;) {
698 while (XPending(display)) {
699 XNextEvent(display, &event);
700 switch (event.type) {
701 case Expose:
702 if (event.xexpose.count == 0) {
703 redrawWindow();
705 break;
706 case ConfigureNotify:
707 redrawWindow();
708 break;
709 case ButtonPress:
711 int nButton= getButtonAt(event.xbutton.x, event.xbutton.y);
712 if (isVerbose()) {
713 fprintf(stdout, "[%8ld] button %d pressed\n", currentTimeMillis(), nButton);
715 if (nButton != -1) {
716 setCurrentScreen(nButton);
717 gotoScreen(nButton);
720 break;
721 case PropertyNotify:
722 if (strcmp(XA_NET_CURRENT_DESKTOP, XGetAtomName(getDisplay(), event.xproperty.atom)) == 0) {
723 setCurrentScreen(-1);
724 if (isVerbose()) {
725 fprintf(stdout, "[%8ld] new current workspace (%d= %s)\n",
726 currentTimeMillis(), getCurrentScreen(), getScreenName(getCurrentScreen()));
728 redrawWindow();
730 break;
731 case DestroyNotify:
732 if (isVerbose()) {
733 fprintf(stdout, "[%8ld] quit application\n", currentTimeMillis());
735 destroyWindow();
736 destroyDisplay();
737 exit(0);
738 break;
741 usleep(50000);
746 * Button Helpers
749 static int _nButtons;
750 static int _nButtonRows, _nButtonColumns;
751 static int _nButtonWidth, _nButtonHeight;
753 int getButtonCount () {
754 return _nButtons;
757 int getButtonRowCount () {
758 return _nButtonRows;
761 int getButtonColumnCount () {
762 return _nButtonColumns;
765 int getButtonWidth () {
766 return _nButtonWidth;
769 int getButtonHeight () {
770 return _nButtonHeight;
773 int getButtonAt (int nLocationX, int nLocationY) {
774 int i, nButtonX, nButtonY;
775 for (i= 0; i < _nButtons; i++) {
776 getButtonLocation(i, &nButtonX, &nButtonY);
777 if (
778 nLocationX >= nButtonX && nLocationX < nButtonX + _nButtonWidth &&
779 nLocationY >= nButtonY && nLocationY < nButtonY + _nButtonHeight
781 return i;
784 return -1;
787 void getButtonLocation (int nButton, int* nLocationX, int* nLocationY) {
788 if (nButton < 0 || nButton > _nButtons) {
789 *nLocationX= *nLocationY= 0;
790 } else {
791 *nLocationX= *nLocationY= 7;
792 while (nButton >= _nButtonColumns) {
793 *nLocationY+= _nButtonHeight + 3;
794 nButton-= _nButtonColumns;
796 while (nButton > 0) {
797 *nLocationX+= _nButtonWidth + 3;
798 nButton--;
803 void initButtons (int nButtons, int nColumns, int nRows) {
804 if (nButtons != -1) {
805 _nButtons= nButtons;
806 } else {
807 _nButtons= getScreenCount();
808 if (_nButtons > 9) {
809 /* only handle nine screens at most */
810 _nButtons= 9;
813 if (nColumns == -1 || nRows == -1) {
814 switch (_nButtons) {
815 case 1:
816 _nButtonRows= _nButtonColumns= 1;
817 break;
818 case 2:
819 _nButtonColumns= 1;
820 _nButtonRows= 2;
821 break;
822 case 3:
823 /* fallthrough */
824 case 4:
825 _nButtonColumns= _nButtonRows= 2;
826 break;
827 case 5:
828 /* fallthrough */
829 case 6:
830 _nButtonColumns= 2;
831 _nButtonRows= 3;
832 break;
833 default:
834 _nButtonColumns= _nButtonRows= 3;
835 break;
837 } else {
838 _nButtonColumns= nColumns;
839 _nButtonRows= nRows;
841 if (_nButtons > _nButtonColumns * _nButtonRows) {
842 _nButtons= _nButtonColumns * _nButtonRows;
844 if (isVerbose()) {
845 fprintf(stdout, "[%8ld] initializing buttons\n", currentTimeMillis());
846 fprintf(stdout, "[%8ld] - %d workspace buttons\n", currentTimeMillis(), _nButtons);
847 fprintf(stdout, "[%8ld] - button layout %dx%d\n", currentTimeMillis(), _nButtonColumns, _nButtonRows);
850 if (_nButtonColumns == 1) {
851 _nButtonWidth= 51;
852 } else if (_nButtonColumns == 2) {
853 _nButtonWidth= 24;
854 } else {
855 _nButtonWidth= 15;
858 if (_nButtonRows == 1) {
859 _nButtonHeight= 51;
860 } else if (_nButtonRows == 2) {
861 _nButtonHeight= 24;
862 } else {
863 _nButtonHeight= 15;
869 * Time
872 static struct timeval _tStart;
874 void initTime () {
875 if (isVerbose()) {
876 fprintf(stdout, "[ ] initializing time\n");
878 gettimeofday(&_tStart, NULL);
881 long currentTimeMillis () {
882 struct timeval tNow;
883 struct timeval tElapsed;
885 gettimeofday(&tNow, NULL);
887 if (_tStart.tv_usec > tNow.tv_usec) {
888 tNow.tv_usec+= 1000000;
889 tNow.tv_sec--;
891 tElapsed.tv_sec= tNow.tv_sec - _tStart.tv_sec;
892 tElapsed.tv_usec= tNow.tv_usec - _tStart.tv_usec;
893 return (tElapsed.tv_sec * 1000) + (tElapsed.tv_usec / 1000);
897 * Screen Handling
900 static Atom _xaNetNumberOfDesktops;
901 static Atom _xaNetCurrentDesktop;
902 static Atom _xaNetDesktopNames;
903 static int _nScreens;
904 static char** _szScreenNames;
905 static int _nDesktopNames;
906 static int _nLastScreen;
908 int getScreenCount () {
909 return _nScreens;
912 char* getScreenName (int nScreen) {
913 if (nScreen < 0 || nScreen >= _nDesktopNames) {
914 return "<empty>";
916 return _szScreenNames[nScreen];
919 void gotoScreen (int nWorkspace) {
920 XEvent event;
921 event.type= ClientMessage;
922 event.xclient.type= ClientMessage;
923 event.xclient.window= getRootWindow();
924 event.xclient.message_type= _xaNetCurrentDesktop;
925 event.xclient.format= 32;
926 event.xclient.data.l[0]= nWorkspace;
927 event.xclient.data.l[1]= currentTimeMillis();
928 XSendEvent(getDisplay(), getRootWindow(), False, SubstructureNotifyMask, (XEvent*) &event);
931 int getCurrentScreen () {
932 return _nLastScreen;
935 void setCurrentScreen (int nCurrentScreen) {
936 if (nCurrentScreen == -1) {
937 long nScreen= _nLastScreen;
938 Atom xaType;
939 int nFormat;
940 unsigned long nItems, nBytesAfter;
941 unsigned char* data;
943 XGetWindowProperty(
944 getDisplay(), getRootWindow(), _xaNetCurrentDesktop,
945 0, 8192, False, XA_CARDINAL, &xaType, &nFormat, &nItems, &nBytesAfter, &data
947 if ((nFormat == 32) && (nItems == 1) && (nBytesAfter == 0)) {
948 nScreen= *(long*) data;
950 if (xaType != None) {
951 XFree(data);
953 _nLastScreen= nScreen;
954 } else {
955 _nLastScreen= nCurrentScreen;
959 void initScreens () {
960 XTextProperty tp;
961 Atom xaType;
962 int nFormat;
963 unsigned long nItems, nBytesAfter;
964 unsigned char* data;
966 if (isVerbose()) {
967 fprintf(stdout, "[%8ld] initializing window maker communication\n", currentTimeMillis());
969 _xaNetNumberOfDesktops= XInternAtom(getDisplay(), XA_NET_NUMBER_OF_DESKTOPS, False);
970 _xaNetCurrentDesktop= XInternAtom(getDisplay(), XA_NET_CURRENT_DESKTOP, False);
971 _xaNetDesktopNames= XInternAtom(getDisplay(), XA_NET_DESKTOP_NAMES, False);
973 XGetWindowProperty(
974 getDisplay(), getRootWindow(), _xaNetNumberOfDesktops,
975 0, 8192, False, XA_CARDINAL, &xaType, &nFormat, &nItems, &nBytesAfter, &data
977 if ((nFormat == 32) && (nItems == 1) && (nBytesAfter == 0)) {
978 _nScreens= *(long*) data;
980 if (xaType != None) {
981 XFree(data);
984 XGetTextProperty(getDisplay(), getRootWindow(), &tp, _xaNetDesktopNames);
985 Xutf8TextPropertyToTextList(getDisplay(), &tp, &_szScreenNames, &_nDesktopNames);
987 _nLastScreen= -1;
988 setCurrentScreen(-1);
989 if (_nLastScreen == -1) {
990 fprintf(
991 stderr,
992 "%s: couldn't determine current workspace.\n" \
993 "Make sure your WindowMaker has EWMH support enabled!\n",
994 getApplicationName()
996 setCurrentScreen(0);
998 if (isVerbose()) {
999 int i;
1000 fprintf(stdout, "[%8ld] - %d worspaces found\n", currentTimeMillis(), getScreenCount());
1001 for (i= 0; i < getScreenCount(); i++) {
1002 fprintf(stdout, "[%8ld] - workspace %d: %s\n", currentTimeMillis(), i, getScreenName(i));
1004 fprintf(stdout, "[%8ld] - current workspace is %d (%s)\n", currentTimeMillis(),
1005 getCurrentScreen(), getScreenName(getCurrentScreen()));