1 /* apm/acpi dockapp - phear it 1.34
2 * Copyright (C) 2000, 2001, 2002 timecop@japan.co.jp
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 /* #define RETARDED_APM */
20 /* #define STUPID_APM */
21 /* see README if you need to #define these or not. No user serviceable
36 #include <X11/Xutil.h>
37 #include <X11/extensions/shape.h>
42 #if defined(ACPI) && defined(APM)
43 # error Cannot compile with ACPI and APM compiled in. Please select only one.
48 #include "master_low.xpm"
54 Display
*display
; /* X11 display struct */
55 int screen
; /* current screen */
56 Window root
; /* root window */
57 Window win
; /* one window */
58 Window iconwin
; /* another one */
59 Pixmap pixmap
; /* UI pixmap, window pixmap */
60 Pixmap mask
; /* mask pixmap for shape */
61 GC gc
; /* main drawing GC */
62 Pixmap text
; /* pixmap for text scroller */
63 int tw
; /* text width inside text pixmap */
64 int update
; /* need to redraw? */
65 int pressed
; /* is the button pressed? */
66 DspMode dspmode
; /* time remaining or battery timer */
67 Mode blink
; /* should we blink the LED? (critical battery) */
70 /* for debug printing */
72 char *state
[] = { "AC", "Charging", "High", "Low", "Crit" };
78 int count
= 0; /* global timer variable */
79 int noisy_critical
= 0; /* ring xbell annoyingly if critical? */
81 /* proto for local stuff */
82 static void new_window(char *name
);
83 static int open_display(char *display
);
84 static void redraw_window(void);
85 static void render_text(char *string
);
86 static void scroll_text(int x
, int y
, int width
, int tw
, int reset
);
87 static void display_percentage(int percent
);
88 static void display_state(void);
89 static void display_time(int minutes
);
90 static void blink_button(Mode mode
);
92 #define copy_xpm_area(x, y, w, h, dx, dy) \
94 XCopyArea(dockapp->display, dockapp->pixmap, dockapp->pixmap, \
95 dockapp->gc, x, y, w, h, dx, dy); \
96 dockapp->update = 1; \
99 static void redraw_window(void)
101 if (dockapp
->update
) {
102 eprint(1, "redrawing window");
103 XCopyArea(dockapp
->display
, dockapp
->pixmap
, dockapp
->iconwin
,
104 dockapp
->gc
, 0, 0, 64, 64, 0, 0);
105 XCopyArea(dockapp
->display
, dockapp
->pixmap
, dockapp
->win
,
106 dockapp
->gc
, 0, 0, 64, 64, 0, 0);
111 static void new_window(char *name
)
116 XSizeHints sizehints
;
117 XClassHint classhint
;
120 dockapp
->screen
= DefaultScreen(dockapp
->display
);
121 dockapp
->root
= DefaultRootWindow(dockapp
->display
);
123 sizehints
.flags
= USSize
;
124 sizehints
.width
= 64;
125 sizehints
.height
= 64;
127 fg
= BlackPixel(dockapp
->display
, dockapp
->screen
);
128 bg
= WhitePixel(dockapp
->display
, dockapp
->screen
);
130 dockapp
->win
= XCreateSimpleWindow(dockapp
->display
, dockapp
->root
,
131 0, 0, sizehints
.width
,
132 sizehints
.height
, 1, fg
, bg
);
134 XCreateSimpleWindow(dockapp
->display
, dockapp
->win
, 0, 0,
135 sizehints
.width
, sizehints
.height
, 1, fg
, bg
);
137 XSetWMNormalHints(dockapp
->display
, dockapp
->win
, &sizehints
);
138 classhint
.res_name
= name
;
139 classhint
.res_class
= name
;
140 XSetClassHint(dockapp
->display
, dockapp
->win
, &classhint
);
142 XSelectInput(dockapp
->display
, dockapp
->win
,
143 ExposureMask
| ButtonPressMask
| ButtonReleaseMask
|
144 StructureNotifyMask
);
145 XSelectInput(dockapp
->display
, dockapp
->iconwin
,
146 ExposureMask
| ButtonPressMask
| ButtonReleaseMask
|
147 StructureNotifyMask
);
149 XStoreName(dockapp
->display
, dockapp
->win
, name
);
150 XSetIconName(dockapp
->display
, dockapp
->win
, name
);
152 gcval
.foreground
= fg
;
153 gcval
.background
= bg
;
154 gcval
.graphics_exposures
= False
;
157 XCreateGC(dockapp
->display
, dockapp
->win
,
158 GCForeground
| GCBackground
| GCGraphicsExposures
,
161 attr
.exactColors
= 0;
162 attr
.alloc_close_colors
= 1;
163 attr
.closeness
= 1L << 15;
164 attr
.valuemask
= XpmExactColors
| XpmAllocCloseColors
| XpmCloseness
;
165 if (XpmCreatePixmapFromData(dockapp
->display
, dockapp
->win
,
166 master_xpm
, &dockapp
->pixmap
,
167 &dockapp
->mask
, &attr
) != XpmSuccess
) {
168 fprintf(stderr
, "FATAL: Not enough colors for main pixmap!\n");
172 /* text area is 318x7, or 53 characters long */
173 dockapp
->text
= XCreatePixmap(dockapp
->display
, dockapp
->win
, 318, 7,
174 DefaultDepth(dockapp
->display
,
176 if (!dockapp
->text
) {
177 fprintf(stderr
, "FATAL: Cannot create text scroll pixmap!\n");
181 XShapeCombineMask(dockapp
->display
, dockapp
->win
, ShapeBounding
, 0, 0,
182 dockapp
->mask
, ShapeSet
);
183 XShapeCombineMask(dockapp
->display
, dockapp
->iconwin
, ShapeBounding
, 0,
184 0, dockapp
->mask
, ShapeSet
);
186 wmhints
.initial_state
= WithdrawnState
;
187 wmhints
.flags
= StateHint
;
188 wmhints
.icon_window
= dockapp
->iconwin
;
189 wmhints
.icon_x
= sizehints
.x
;
190 wmhints
.icon_y
= sizehints
.y
;
191 wmhints
.window_group
= dockapp
->win
;
193 StateHint
| IconWindowHint
| IconPositionHint
| WindowGroupHint
;
194 XSetWMHints(dockapp
->display
, dockapp
->win
, &wmhints
);
196 XMapWindow(dockapp
->display
, dockapp
->win
);
199 static void render_text(char *string
)
203 if (strlen(string
) > 53)
206 eprint(1, "rendering: %s", string
);
208 /* prepare the text area by clearing it */
209 for (i
= 0; i
< 54; i
++) {
210 XCopyArea(dockapp
->display
, dockapp
->pixmap
, dockapp
->text
,
211 dockapp
->gc
, 133, 57, 6, 8, i
* 6, 0);
215 for (i
= 0; string
[i
]; i
++) {
216 c
= toupper(string
[i
]);
217 if (c
>= 'A' && c
<= 'Z') { /* letter */
219 XCopyArea(dockapp
->display
, dockapp
->pixmap
, dockapp
->text
,
220 dockapp
->gc
, c
* 6, 67, 6, 7, k
, 0);
221 } else if (c
>= '0' && c
<= '9') { /* number */
223 XCopyArea(dockapp
->display
, dockapp
->pixmap
, dockapp
->text
,
224 dockapp
->gc
, c
* 6 + 66, 58, 6, 7, k
, 0);
225 } else if (c
== '.') {
226 XCopyArea(dockapp
->display
, dockapp
->pixmap
, dockapp
->text
,
227 dockapp
->gc
, 140, 58, 6, 7, k
, 0);
228 } else if (c
== '-') {
229 XCopyArea(dockapp
->display
, dockapp
->pixmap
, dockapp
->text
,
230 dockapp
->gc
, 126, 58, 6, 7, k
, 0);
234 dockapp
->tw
= k
; /* length of text segment */
235 /* re-scroll the message */
236 scroll_text(6, 50, 52, dockapp
->tw
, 1);
237 /* reset the scroll repeat counter */
241 static int open_display(char *display
)
243 dockapp
->display
= XOpenDisplay(display
);
244 if (!dockapp
->display
) {
245 fprintf(stderr
, "Unable to open display '%s'\n", display
);
251 static void scroll_text(int x
, int y
, int width
, int tw
, int reset
)
253 static int pos
, first
, stop
;
259 XCopyArea(dockapp
->display
, dockapp
->pixmap
, dockapp
->text
,
260 dockapp
->gc
, 0, 0, width
, 7, x
, y
);
267 if ((first
== 0) && pos
== 0) {
272 if (pos
== (0 - tw
- 2)) {
280 eprint(0, "scrolling");
283 copy_xpm_area(66, 9, pos
, 7, x
, y
); /* clear */
284 XCopyArea(dockapp
->display
, dockapp
->text
, dockapp
->pixmap
,
285 dockapp
->gc
, 0, 0, width
- pos
, 7, x
+ pos
, y
);
286 } else { /* don't need to clear, already in text */
287 XCopyArea(dockapp
->display
, dockapp
->text
, dockapp
->pixmap
,
288 dockapp
->gc
, abs(pos
), 0, width
, 7, x
, y
);
293 static void display_percentage(int percent
)
295 static int op
= -1, obar
;
298 eprint(1, "received: %d\n", percent
);
308 if (percent
< 100) { /* 0 - 99 */
309 copy_xpm_area(95, 48, 8, 7, 37, 17);
311 copy_xpm_area((percent
/ 10) * 6 + 67, 28, 5, 7, 40, 17);
312 copy_xpm_area((percent
% 10) * 6 + 67, 28, 5, 7, 46, 17);
314 copy_xpm_area(95, 37, 21, 9, 37, 16); /* 100% */
317 bar
= percent
/ 1.8518;
322 copy_xpm_area(66, 0, bar
, 8, 5, 5);
324 copy_xpm_area(66 + bar
, 18, 54 - bar
, 8, bar
+ 5, 5);
328 static void display_time(int minutes
)
330 static int ohour
= -1, omin
= -1;
334 if (minutes
== -1) { /* error - blink 00:00 */
337 copy_xpm_area(80, 76, 31, 11, 7, 32);
338 } else if (counter
== 10) {
339 copy_xpm_area(114, 76, 31, 11, 7, 32);
347 /* render time on the display */
351 if (hour
== ohour
&& min
== omin
)
354 eprint(0, "redrawing time");
356 copy_xpm_area(tmp
* 7 + 1, 76, 6, 11, 7, 32);
358 copy_xpm_area(tmp
* 7 + 1, 76, 6, 11, 14, 32);
360 copy_xpm_area(tmp
* 7 + 1, 76, 6, 11, 25, 32);
362 copy_xpm_area(tmp
* 7 + 1, 76, 6, 11, 32, 32);
363 copy_xpm_area(71, 76, 3, 11, 21, 32);
368 static void display_state(void)
371 static int docharging
;
372 static int dobattery
;
373 static int docritical
;
376 switch (apminfo
->power
) {
378 eprint(0, "selected ac power case");
383 dockapp
->blink
= OFF
;
384 copy_xpm_area(67, 38, 12, 7, 6, 17);
385 copy_xpm_area(82, 48, 11, 7, 20, 17);
386 render_text("On AC power");
390 eprint(0, "selected charging case");
393 copy_xpm_area(67, 38, 12, 7, 6, 17);
394 } else if (counter
== 20) {
395 copy_xpm_area(67, 48, 12, 7, 6, 17);
400 render_text("Battery is charging");
401 /* get rid of battery symbol */
402 copy_xpm_area(82, 48, 12, 7, 20, 17);
404 dockapp
->blink
= OFF
;
413 eprint(0, "selected battery case");
415 render_text("On Battery");
416 /* display battery symbol */
417 copy_xpm_area(82, 38, 12, 7, 20, 17);
418 /* get rid of AC power symbol */
419 copy_xpm_area(67, 48, 12, 7, 6, 17);
424 if (apminfo
->power
== CRIT
) {
425 dockapp
->blink
= BLINK
;
427 render_text("Battery Critical Low");
432 render_text("On Battery");
435 dockapp
->blink
= OFF
;
441 static void blink_button(Mode mode
)
446 if ((mode
== OFF
) && !clear
) {
447 eprint(0, "we are off");
448 copy_xpm_area(136, 38, 3, 3, 44, 30);
458 copy_xpm_area(137, 33, 3, 3, 44, 30);
460 } else if (counter
== 10) {
461 copy_xpm_area(136, 38, 3, 3, 44, 30);
463 /* make some noise */
465 XBell(dockapp
->display
, 100);
471 int main(int argc
, char **argv
)
473 char *display
= NULL
;
477 dockapp
= calloc(1, sizeof(Dockapp
));
478 apminfo
= calloc(1, sizeof(APMInfo
));
480 dockapp
->blink
= OFF
;
481 apminfo
->crit_level
= 10;
483 /* see if whatever we want to use is supported */
485 /* power_init functions handle printing error messages */
489 /* parse command-line options */
490 while ((ch
= getopt(argc
, argv
, "bd:c:h")) != EOF
) {
497 apminfo
->crit_level
= atoi(optarg
);
498 if ((apminfo
->crit_level
< 0) || (apminfo
->crit_level
> 100)) {
499 fprintf(stderr
, "Please use values between 0 and 100%%\n");
500 apminfo
->crit_level
= 10;
501 fprintf(stderr
, "Using default value of 10%%\n");
507 display
= strdup(optarg
);
510 printf("wmacpi - help\t\t[timecop@japan.co.jp]\n\n"
511 "-d display\t\tdisplay on remote display <display>\n"
512 "-b\t\t\tmake noise when battery is critical low (beep)\n"
513 "-c value\t\tset critical low alarm at <value> percent\n"
514 "\t\t\t(default: 10 percent)\n"
515 "-h\t\t\tdisplay this help\n");
522 /* open local or command-line specified display */
523 if (open_display(display
))
526 /* make new dockapp window */
529 /* get initial statistics */
532 dockapp
->dspmode
= REMAIN
;
537 while (XPending(dockapp
->display
)) {
538 eprint(0, "X11 activity");
539 XNextEvent(dockapp
->display
, &event
);
540 switch (event
.type
) {
544 while (XCheckTypedEvent(dockapp
->display
, Expose
, &event
));
548 XCloseDisplay(dockapp
->display
);
553 if (event
.xbutton
.x
>= 44 && event
.xbutton
.x
<= 57 &&
554 event
.xbutton
.y
>= 30 && event
.xbutton
.y
<= 43) {
555 eprint(0, "inside button!");
556 dockapp
->pressed
= 1;
557 copy_xpm_area(118, 38, 15, 15, 44, 30);
562 if (event
.xbutton
.x
>= 44 && event
.xbutton
.x
<= 57 &&
563 event
.xbutton
.y
>= 30 && event
.xbutton
.y
<= 43 &&
565 /* handle button press */
566 eprint(0, "release still inside button!");
567 dockapp
->pressed
= 0;
568 copy_xpm_area(136, 38, 15, 15, 44, 30);
569 if ((apminfo
->power
!= POWER
) && (apminfo
->power
!= CHARGING
)) {
570 dockapp
->dspmode
= !dockapp
->dspmode
;
571 eprint(1, "Mode: %d", dockapp
->dspmode
);
573 /* end button press handler */
575 if (dockapp
->pressed
) {
576 copy_xpm_area(136, 38, 15, 15, 44, 30);
577 dockapp
->pressed
= 0;
583 if (update
++ == 30) {
584 eprint(1, "polling apm");
589 if (count
++ == 256) {
590 scroll_text(6, 50, 52, dockapp
->tw
, 1);
594 /* it's okay to test here because display_time will not draw anything
595 * unless there is a change. Also if we switched power states from
596 * battery to charging/etc, we need to exit from "timer" mode */
597 if (dockapp
->dspmode
== REMAIN
|| apminfo
->power
== POWER
|| apminfo
->power
== CHARGING
) {
598 display_time(apminfo
->rtime
);
600 display_time((time(NULL
) - apminfo
->timer
) / 60);
604 blink_button(dockapp
->blink
);
605 display_percentage(apminfo
->percentage
);
606 scroll_text(6, 50, 52, dockapp
->tw
, 0);
608 /* redraw_window, if anything changed - determined inside
616 /* this handles enabling "on-battery" timer. It only needs to happen once
617 * for each unplug event. Functions from libapm and libacpi call this to
619 void process_plugin_timer(void)
623 if ((apminfo
->power
!= POWER
) && (apminfo
->power
!= CHARGING
) && !timer
) {
624 eprint(1, "not AC and not charging, and timer is not started");
625 eprint(1, "starting battery timer");
626 apminfo
->timer
= time(NULL
);
629 if (((apminfo
->power
== POWER
) || (apminfo
->power
== CHARGING
)) && timer
) {
630 eprint(1, "disabling battery timer");