/* * xbatt -- Display battery status for FreeBSD-2.x & Linux * * Copyright (c) 1995,1997 by Toshihisa ETO * * * This software may be used, modified, copied, and distributed, in * both source and binary form provided that the above copyright and * these terms are retained. Under no circumstances is the author * responsible for the proper functioning of this software, nor does * the author assume any responsibility for damages incurred with its * use. * */ /* * HISTORY * 95/03/27 0.1 XBM version * 95/03/28 0.2 refine bitmap management * 95/03/30 0.3 use XPM library * 95/04/01 0.3.1 by Noriyuki Takahashi (nor@aecl.ntt.jp) * Display plug and charging status. * Support gray-scale and mono (compile-time option). * 95/04/03 0.4 Support gray-scale and mono (auto detect). * Update status, when catch key event. * 95/04/19 1.0 Official release with some documents!!!! * 95/04/27 1.1 Support Linux apm-bios 0.5 * by pcs00294@asciinet.or.jp (Koyama Tadayoshi) * 97/01/01 1.2b1 Modify some place * 97/01/17 1.2b3 Support Linux-1.3.58 or lator * by hisama@bunff.trad.pfu.co.jp * mac@pssys.flab.fujitsu.co.jp * yoshi@funival.mfd.cs.fujitsu.co.jp * Support for PC which does not return * buttery life (e.g. ThinkPad 535) * by umemoto * Add check timer routine for resume problem * Show battery life, when the mouse * cursor located on battery icon. * 97/01/20 1.2b4 Changes for Xt related part (e.g. resources) * by mi@sy.ssl.fujitsu.co.jp * Change small bugs */ #include #include #include #include #include #include #include #include #include #include #include #ifdef XKB # include #endif #ifdef __FreeBSD__ # include # define APMDEV21 "/dev/apm0" # define APMDEV22 "/dev/apm" #endif #include "pixmaps/battery.xpm" #include "pixmaps/unknown.xpm" #include "bitmaps/full.xbm" #include "bitmaps/digit0.xbm" #include "bitmaps/digit1.xbm" #include "bitmaps/digit2.xbm" #include "bitmaps/digit3.xbm" #include "bitmaps/digit4.xbm" #include "bitmaps/digit5.xbm" #include "bitmaps/digit6.xbm" #include "bitmaps/digit7.xbm" #include "bitmaps/digit8.xbm" #include "bitmaps/digit9.xbm" #include "bitmaps/percent.xbm" /* reflash Interval (in seconds) */ #ifndef UPDATE_INTERVAL # ifndef NOAPM # define UPDATE_INTERVAL 30 # else # define UPDATE_INTERVAL 5 # endif #endif #define APM_STAT_UNKNOWN 255 /* aip->ai_acline */ #define APM_STAT_LINE_OFF 0 #define APM_STAT_LINE_ON 1 /* aip->ai_batt_stat */ #define APM_STAT_BATT_HIGH 0 #define APM_STAT_BATT_LOW 1 #define APM_STAT_BATT_CRITICAL 2 #define APM_STAT_BATT_CHARGING 3 struct status { u_int remain; u_int acline; u_int charge; }; static struct status lastStat = {APM_STAT_UNKNOWN, APM_STAT_UNKNOWN, APM_STAT_UNKNOWN}; static void updateStatus(XtPointer, XtIntervalId*); static struct status getBatteryStatus(); static void updateWindow(struct status); static void initWindowResource(); #define BatteryWidth 39 #define BatteryHeight 39 static void setColorSymbol(unsigned int, unsigned int, unsigned int); static void goOut(); static void CallbackTyped(Widget, char*, XEvent*, Boolean*); static void CallbackEnter(Widget, char*, XEvent*, Boolean*); static void CallbackLeave(Widget, char*, XEvent*, Boolean*); static time_t lastUpdateTime = 0; static int showStatus = 0; static int forceRedraw = 0; static int apmfd = 0; static Widget toplevel; static Pixmap xpmData, xpmMask; static GC gc; static XpmAttributes attr; static XtIntervalId timer = 0; static int displayType; /* Mono, Gray, Color */ #define Color 0 #define Gray 1 #define Mono 2 #define MAXSYM 30 static XpmColorSymbol sym[MAXSYM]; static unsigned int nsym = 0; typedef struct _SetupRec{ int timerInterval; } SetupRec, *SetupPtr; static SetupRec appResources; static XtResource resources[] = { {"timerInterval", "TimerInterval", XtRInt, sizeof(int), XtOffsetOf(struct _SetupRec, timerInterval), XtRImmediate, (XtPointer)UPDATE_INTERVAL}, }; static XrmOptionDescRec options[] = { {"-timerInterval", ".timerInterval", XrmoptionSepArg, (XtPointer)UPDATE_INTERVAL}, }; struct Digits { unsigned int width; unsigned int height; unsigned char* bits; }; struct Digits digits[] = { {digit0_width, digit0_height, digit0_bits}, {digit1_width, digit1_height, digit1_bits}, {digit2_width, digit2_height, digit2_bits}, {digit3_width, digit3_height, digit3_bits}, {digit4_width, digit4_height, digit4_bits}, {digit5_width, digit5_height, digit5_bits}, {digit6_width, digit6_height, digit6_bits}, {digit7_width, digit7_height, digit7_bits}, {digit8_width, digit8_height, digit8_bits}, {digit9_width, digit9_height, digit9_bits} }; main( int argc, char *argv[] ) { XSizeHints size_hints; Visual* visual; int depth; XtAppContext appContext; #if !defined(NOAPM) && defined(__FreeBSD__) /* initialize APM Interface */ if ((apmfd = open(APMDEV21, O_RDWR)) == -1) { if ((apmfd = open(APMDEV22, O_RDWR)) == -1) { fprintf(stderr, "xbatt: cannot open apm device (%s or %s): %s\n", APMDEV21, APMDEV22, strerror(errno)); exit(1); } } #endif /* start X-Window session */ XtSetLanguageProc( NULL, NULL, NULL ); toplevel = XtOpenApplication(&appContext, "XBatt", options, XtNumber(options), &argc, argv, NULL, sessionShellWidgetClass, NULL, ZERO); if (!toplevel) { fprintf(stderr, "xbatt: cannot start x-session\n"); exit(1); } if (argc != 1) { fprintf(stderr, "usage: xbatt [X-Toolkit standard option]\n"); exit(1); } /* get rsource information */ XtGetApplicationResources(toplevel, &appResources, resources, XtNumber(resources), NULL, 0); /* get display information */ visual = XDefaultVisual(XtDisplay(toplevel), DefaultScreen(XtDisplay(toplevel))); depth = XDefaultDepth(XtDisplay(toplevel), DefaultScreen(XtDisplay(toplevel))); if (depth == 1) { displayType = Mono; } else { switch(visual->class) { case PseudoColor: case DirectColor: case StaticColor: case TrueColor: displayType = Color; break; case GrayScale: case StaticGray: displayType = Gray; break; default: displayType = Mono; } } /* set toplevel property */ XtVaSetValues(toplevel, XtNmappedWhenManaged, False, XtNinput, True, XtNwidth, BatteryWidth, XtNheight,BatteryHeight, XtNminWidth, BatteryWidth, XtNminHeight, BatteryHeight, XtNiconName, "xbatt", NULL); /* realize toplevel widget */ XtRealizeWidget(toplevel); /* set window information and callback procedure */ initWindowResource(); XtAddEventHandler(toplevel, KeyPressMask, False, (XtEventHandler)CallbackTyped, NULL); XtAddEventHandler(toplevel, EnterWindowMask, False, (XtEventHandler)CallbackEnter, NULL); XtAddEventHandler(toplevel, LeaveWindowMask, False, (XtEventHandler)CallbackLeave, NULL); /* display current battery status, and set timer callback */ updateStatus(NULL, NULL); /* start main loop */ XtAppMainLoop(appContext); /* free resource, and go out*/ goOut(0); } void updateStatus( XtPointer client_data, XtIntervalId *t ) { struct status s; /* if this function called by another function, */ /* then remove current timer */ if ((t == NULL) && (timer != 0)) { XtRemoveTimeOut(timer); timer = 0; } /* get and update current status */ s = getBatteryStatus(); /* if status has changed, update window */ if (((s.remain != lastStat.remain) && (((s.remain % 5) == 0) || showStatus != 0)) || (s.acline != lastStat.acline) || (s.charge != lastStat.charge) || (forceRedraw != 0) ) { /* update window */ updateWindow(s); /* remember current status */ lastStat = s; forceRedraw = 0; } /* start timer for next interval */ timer = XtAppAddTimeOut(XtWidgetToApplicationContext(toplevel), appResources.timerInterval * 1000, updateStatus, toplevel); /* save time stamp */ lastUpdateTime = time(NULL); } struct status getBatteryStatus() { struct status ret; #ifdef __FreeBSD__ struct apm_info info; #ifndef NOAPM /* get APM information */ if (ioctl(apmfd, APMIO_GETINFO, &info) == -1) { fprintf(stderr, "xbatt: ioctl APMIO_GETINFO failed\n"); exit(1); } #else /* NOAPM */ /* set dummy battery life */ if ((lastStat.remain < 1) || (lastStat.remain > 10)) { info.ai_batt_life = 100; } else { info.ai_batt_life = (lastStat.remain - 1) * 10; } #endif /* NOAPM */ /* get current status */ if (info.ai_batt_life == APM_STAT_UNKNOWN) { switch (info.ai_batt_stat) { case APM_STAT_BATT_HIGH: ret.remain = 100; break; case APM_STAT_BATT_LOW: ret.remain = 40; break; case APM_STAT_BATT_CRITICAL: ret.remain = 10; break; default: /* expected to be APM_STAT_UNKNOWN */ ret.remain = APM_STAT_UNKNOWN; } } else if (info.ai_batt_life > 100) { /* some APM BIOSes return values slightly > 100 */ ret.remain = 100; } else { ret.remain = info.ai_batt_life; } /* get AC-line status */ if (info.ai_acline == APM_STAT_LINE_ON) { ret.acline = APM_STAT_LINE_ON; } else { ret.acline = APM_STAT_LINE_OFF; } /* get charging status */ if (info.ai_batt_stat == APM_STAT_BATT_CHARGING) { ret.charge = APM_STAT_BATT_CHARGING; } else { ret.charge = APM_STAT_BATT_HIGH; /* I only want to know, */ /* chrging or not. */ } #endif /* FreeBSD */ #ifdef __linux__ char buffer[64]; FILE* fp; int battLife; char driver_version[64]; int apm_bios_info_major; int apm_bios_info_minor; int apm_bios_info_flags; int ac_line_status; int battery_status; int battery_flag; int time_units; char units[64]; /* open power status */ if ((fp = fopen("/proc/apm", "r")) == NULL) { fprintf(stderr, "xbatt: cannot optn /proc/apm\n"); exit(1); } ret.charge = APM_STAT_BATT_HIGH; ret.acline = APM_STAT_LINE_OFF; while (fgets(buffer, 64, fp) != NULL) { /* * for linux-1.3.58 or later */ if (sscanf(buffer, "%s %d.%d 0x%x 0x%x 0x%x 0x%x %d%% %d %s\n", driver_version, &apm_bios_info_major, &apm_bios_info_minor, &apm_bios_info_flags, &ac_line_status, &battery_status, &battery_flag, &battLife, &time_units, units) == 10) { if (battLife < 0) { ret.remain = APM_STAT_UNKNOWN; } else { ret.remain = battLife; } if (ac_line_status == 0x01) { ret.acline = APM_STAT_LINE_ON; } if (battery_status == 0x03) { ret.charge = APM_STAT_BATT_CHARGING; } } /* * for apm_bios-0.5 */ else if (sscanf(buffer, "Battery life: %d%%\n", &battLife) == 1) { ret.remain = battLife; } else if (strcmp("AC: on line\n", buffer)==0) { ret.acline = APM_STAT_LINE_ON; } else if (strcmp("Battery status: charging\n", buffer) == 0) { ret.charge = APM_STAT_BATT_CHARGING; } } fclose(fp); #endif /* Linux */ return ret; } void initWindowResource() { gc = XCreateGC(XtDisplay(toplevel), XtWindow(toplevel), 0, 0); XSetState(XtDisplay(toplevel), gc, 1, 0, GXcopy, AllPlanes); XSetFillStyle(XtDisplay(toplevel), gc, FillSolid); XFlush(XtDisplay(toplevel)); } void updateWindow(struct status s) { Pixmap bmp; int ret; /* free old data */ if (xpmData) { XFreePixmap(XtDisplay(toplevel), xpmData); } if (xpmMask) { XFreePixmap(XtDisplay(toplevel), xpmMask); } XpmFreeAttributes(&attr); /* set attribute value mask */ attr.valuemask = 0; attr.valuemask |= XpmReturnPixels; attr.valuemask |= XpmReturnExtensions; attr.valuemask |= XpmColorSymbols; /* set color symbol for current status */ setColorSymbol(s.remain, s.acline, s.charge); /* set color_class flag */ if (displayType == Mono) { attr.valuemask |= XpmColorKey; attr.color_key = XPM_MONO; } else if (displayType == Gray) { attr.valuemask |= XpmColorKey; attr.color_key = XPM_GRAY; } /* create pixmap data */ if (s.remain != APM_STAT_UNKNOWN) { ret = XpmCreatePixmapFromData(XtDisplay(toplevel), XtWindow(toplevel), batt_xpm, &xpmData, &xpmMask, &attr); } else { ret = XpmCreatePixmapFromData(XtDisplay(toplevel), XtWindow(toplevel), ubatt_xpm, &xpmData, &xpmMask, &attr); } switch (ret) { case XpmSuccess: break; case XpmColorError: fprintf(stderr, "xbatt: Could not parse or alloc requested color\n"); break; case XpmNoMemory: fprintf(stderr, "xbatt: Not enough memory\n"); goOut(1); break; case XpmColorFailed: fprintf(stderr, "xbatt: Failed to parse or alloc some color\n"); goOut(1); break; } /* resize window size to pixmap size */ XtResizeWidget(toplevel, attr.width, attr.height, 1); /* if show status is active, then draw status info */ if ((showStatus != 0) && (s.remain != APM_STAT_UNKNOWN)) { Pixmap bm; XSetForeground(XtDisplay(toplevel), gc, BlackPixel(XtDisplay(toplevel), DefaultScreen(XtDisplay(toplevel)))); XDrawRectangle(XtDisplay(toplevel), xpmData, gc, 17, 17, 14, 8); XSetForeground(XtDisplay(toplevel), gc, WhitePixel(XtDisplay(toplevel), DefaultScreen(XtDisplay(toplevel)))); XFillRectangle(XtDisplay(toplevel), xpmData, gc, 18, 18, 13, 7); if (s.remain == 100) { bm = XCreatePixmapFromBitmapData(XtDisplay(toplevel), XtWindow(toplevel), full_bits, full_width, full_height, BlackPixel(XtDisplay(toplevel), DefaultScreen( XtDisplay(toplevel))), WhitePixel(XtDisplay(toplevel), DefaultScreen( XtDisplay(toplevel))), 1); /* Depth */ XCopyPlane(XtDisplay(toplevel), bm, xpmData, gc, 0, 0, full_width, full_height, 19, 19, 1); XFreePixmap(XtDisplay(toplevel), bm); } else { int d = s.remain / 10; bm = XCreatePixmapFromBitmapData(XtDisplay(toplevel), XtWindow(toplevel), digits[d].bits, digits[d].width, digits[d].height, BlackPixel(XtDisplay(toplevel), DefaultScreen( XtDisplay(toplevel))), WhitePixel(XtDisplay(toplevel), DefaultScreen( XtDisplay(toplevel))), 1); XCopyPlane(XtDisplay(toplevel), bm, xpmData, gc, 0, 0, 3, 5, 19, 19, 1); XFreePixmap(XtDisplay(toplevel), bm); d = s.remain % 10; bm = XCreatePixmapFromBitmapData(XtDisplay(toplevel), XtWindow(toplevel), digits[d].bits, digits[d].width, digits[d].height, BlackPixel(XtDisplay(toplevel), DefaultScreen( XtDisplay(toplevel))), WhitePixel(XtDisplay(toplevel), DefaultScreen( XtDisplay(toplevel))), 1); XCopyPlane(XtDisplay(toplevel), bm, xpmData, gc, 0, 0, 3, 5, 23, 19, 1); XFreePixmap(XtDisplay(toplevel), bm); bm = XCreatePixmapFromBitmapData(XtDisplay(toplevel), XtWindow(toplevel), percent_bits, percent_width, percent_height, BlackPixel(XtDisplay(toplevel), DefaultScreen( XtDisplay(toplevel))), WhitePixel(XtDisplay(toplevel), DefaultScreen( XtDisplay(toplevel))), 1); XCopyPlane(XtDisplay(toplevel), bm, xpmData, gc, 0, 0, 3, 5, 27, 19, 1); XFreePixmap(XtDisplay(toplevel), bm); } } /* set pixmap data */ XSetWindowBackgroundPixmap(XtDisplay(toplevel), XtWindow(toplevel), xpmData); /* if pixmap has transparent pixel, set mask pixmap */ if (xpmMask) { XShapeCombineMask(XtDisplay(toplevel), XtWindow(toplevel), ShapeBounding, 0, 0, xpmMask, ShapeSet); } /* redraw window */ XClearWindow(XtDisplay(toplevel), XtWindow(toplevel)); /* map window */ XtMapWidget(toplevel); } void setColorSymbol( unsigned int life, unsigned int acline, unsigned int charge ) { unsigned int i; char* gage_color; life = (life + 9) / 10; /* If battery life is shore, ring a bell */ if ((life != APM_STAT_UNKNOWN) && (life < 5) && ((life % 5) == 0)) { #ifdef XKB XkbStdBell(XtDisplay(toplevel), XtWindow(toplevel), 0, XkbBI_MinorError); #else XBell(XtDisplay(toplevel), 100); #endif } /* clear old data */ for(i = 0; i < nsym; i++) { if (sym[i].name != NULL) { free(sym[i].name); sym[i].name = NULL; } } /* if the battery life is not UNKNOWN status */ if (life != APM_STAT_UNKNOWN) { /* set default(High life) gage color */ switch(displayType) { case Mono: gage_color = "black"; break; case Gray: gage_color = "grey80"; break; case Color: gage_color = "green"; } /* set gage color */ if (displayType == Color) { if (life < 3) { gage_color = "red"; } else if (life < 5) { gage_color = "yellow"; } } /* set symbol for gage */ nsym = 0; for(i = 1; i <= life; i++) { sym[nsym].name = (char*)malloc(7); sprintf(sym[nsym].name, "gage%d", i); sym[nsym].value = gage_color; nsym++; } } /* set symbol for AC-plug */ if(acline != APM_STAT_LINE_ON) { sym[nsym].name = (char*)malloc(5); strcpy(sym[nsym].name, "plug"); sym[nsym].value = "None"; nsym++; } /* set symbol for AC-plug */ if(charge != APM_STAT_BATT_CHARGING) { sym[nsym].name = (char*)malloc(6); strcpy(sym[nsym].name, "spark"); sym[nsym].value = "None"; nsym++; } /* set array to pixmap attribute */ attr.colorsymbols = sym; attr.numsymbols = nsym; } void goOut( int i ) { Arg arg; /* if already running XtTimer, then remove it */ if (timer != 0) { XtRemoveTimeOut(timer); } /* resign from the session */ XtSetArg(arg, XtNjoinSession, False); XtSetValues(toplevel, &arg, ONE); /* free pixmap resources */ if (xpmData) { XFreePixmap(XtDisplay(toplevel), xpmData); } if (xpmMask) { XFreePixmap(XtDisplay(toplevel), xpmMask); } XpmFreeAttributes(&attr); /* close apm fd */ close(apmfd); XCloseDisplay(XtDisplay(toplevel)); exit(i); } void CallbackTyped( Widget widget, char* tag, XEvent* xe, Boolean* b ) { char c; /* get key character */ c = '\0'; XLookupString(&(xe->xkey), &c, 1, NULL, NULL); /* if place `q' key, then just quit */ if (c == 'q' || c == 'Q') { goOut(0); } /* display current battery status, and set timer callback */ updateStatus(NULL, NULL); } void CallbackEnter( Widget widget, char* tag, XEvent* xe, Boolean* b ) { /* set status flag */ showStatus = 1; /* display current battery status, and set timer callback */ forceRedraw = 1; updateStatus(NULL, NULL); /* fprintf(stderr, ">>> Enter [%d]\n", s.remain);*/ } void CallbackLeave( Widget widget, char* tag, XEvent* xe, Boolean* b ) { /* reset status flag */ showStatus = 0; /* display current battery status, and set timer callback */ forceRedraw = 1; updateStatus(NULL, NULL); /* fprintf(stderr, "<<< Leave\n");*/ }