/* * This file is part of firk's window manager for x11 (fwm) * Copyright (C) 2016-2023 firk * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include "main.h" #include "base.h" #include "config.h" #include "winlist.h" #include "fsysd.h" #include "taskbar.h" #include "taskbart.h" #include "xkrap.h" #include "xsupp.h" #define win taskbar_window #define gc taskbar_gc #define cur_x0 taskbar_x0 #define cur_y0 taskbar_y0 #define cur_w taskbar_width #define cur_h taskbar_height typedef struct { int present; unsigned int x0, y0, w, h; } control_info; static control_info bat, bl, cal, trc; static int cal_redraw; /* 1=may be resized; 2=force redraw; 3=force redraw not waiting for the next second */ static int cal_active; /* 0=passively displaying current month; 1=interactive mode */ static unsigned cal_Y, cal_Mpos, cal_MW; /* for interactive mode */ static unsigned cal_py0, cal_px0, cal_px1, cal_px2, cal_px3; /* Mpos is (M-1)*100 for smooth scrolling, allowed values are 0..1199 50=100% January 152=98% Feb 2% Mar 1186=64% Dec 36% next-Jan */ static unsigned mdays(unsigned Y, unsigned M) { /* support M=0 (prev. year December and M=13 (next year January) */ static unsigned const md[14] = {31,31,28,31,30,31,30,31,31,30,31,30,31,31}; if(M==2 && (Y%4)==0 && ((Y%400)==0 || (Y%100)!=0)) return 29; return md[M]; } static void fill_rect(ulong c, int x0, int y0, unsigned w, unsigned h) { XSetForeground(display, gc, c); XFillRectangle(display, win, gc, x0, y0, w, h); } static void move_fill_rect(ulong oc, int ox0, int oy0, unsigned int ow, unsigned int oh, ulong c, int x0, int y0, unsigned w, unsigned h) { /* TODO: avoid double-filling new region */ if(ox0!=x0 || oy0!=y0 || ow!=w || oh!=h) { XSetForeground(display, gc, oc); XFillRectangle(display, win, gc, ox0, oy0, ow, oh); } XSetForeground(display, gc, c); XFillRectangle(display, win, gc, x0, y0, w, h); } /* XSetForeground(display, gc, base_color(CALENDAR_BG)); if(!cal.present || cal.x0==CAL_X && cal.w==CAL_SX && cal.y0==CAL_Y && cal.h==CAL_SY) { XFillRectangle(display, win, gc, CAL_X, CAL_Y, CAL_SX, CAL_SY); } else { // cal.coords will be rewriten below do we may reuse them temporarily cal.w += cal.x0; cal.h += cal.y0; j = CAL_X+CAL_SX; if(cal.wCAL_Y) cal.y0 = CAL_Y; XFillRectangle(display, win, gc, 0, cal.y0, cal.w, cal.h-cal.y0); } */ static void taskbart_draw_passive_calendar(unsigned W, unsigned Y, unsigned M, unsigned D) { char tmps[30]; unsigned CAL_X, CAL_Y, CAL_SX, CAL_SY, CELL_SX, CELL_SY, MAXX, dx, dy; unsigned i, j, k, w, j0; int hd; MAXX = cur_w; CAL_X = CALENDAR_X; CAL_Y = CALENDAR_Y; CELL_SX = CALENDAR_CELLSX; CELL_SY = CALENDAR_CELLSY; if(trc.present && trc.x0>MAXX/2 && trc.x0CAL_Y) MAXX = trc.x0; if(MAXX<40) { if(cal.present) { cal.present = 0; XFillRectangle(display, win, gc, cal.x0, cal.y0, cal.w, cal.h); } return; } while(CAL_X*2+CELL_SX*7>=MAXX) { if(CAL_X*4>=CELL_SX*3 || CAL_X*3>=CELL_SX*2 && CELL_SX<=10) CAL_X--; else CELL_SX--; } CAL_SX = CELL_SX*7; CAL_SY = CELL_SY*6; if(cal.present && cal.x0==CAL_X && cal.y0==CAL_Y && cal.w==CAL_SX && cal.h==CAL_SY && cal_redraw<2) return; if(!cal.present) fill_rect(base_color(CALENDAR_BG), CAL_X, CAL_Y, CAL_SX, CAL_SY); else move_fill_rect(base_color(TASKBAR_BG), cal.x0, cal.y0, cal.w, cal.h, base_color(CALENDAR_BG), CAL_X, CAL_Y, CAL_SX, CAL_SY); cal.x0 = CAL_X; cal.y0 = CAL_Y; cal.w = CAL_SX; cal.h = CAL_SY; cal.present = 1; XSetBackground(display, gc, base_color(CALENDAR_BG)); if(W==0) W=7; W--; assert(M>=1 && M<=12); j = (W+7-(D%7)+1)%7; w = 0; if(j) { k = mdays(Y,M-1); i = k-(j-1); for(j0=0; j00 || hd==-1 && j0>=5) XSetForeground(display, gc, base_color(CALENDAR_OTHERHDAY)); else XSetForeground(display, gc, base_color(CALENDAR_OTHERDAY)); snprintf(tmps, sizeof(tmps), "%u", i); dx = (i>=10)?10:6; if(dx>=CELL_SX) dx = 0; else dx = (CELL_SX-dx)/2; dy = 10; if(dy>=CELL_SY) dy = CELL_SY; else dy = (CELL_SY*4+dy*6)/10; XDrawImageString(display, win, gc, CAL_X+CELL_SX*j0+dx, CAL_Y+CELL_SY*w+dy, tmps, strlen(tmps)); } } i = 1; k = mdays(Y,M); while(1) { if(i==D) { XSetForeground(display, gc, base_color(CALENDAR_CURDAYBG)); XFillRectangle(display, win, gc, CAL_X+CELL_SX*j, CAL_Y+CELL_SY*w, CELL_SX, CELL_SY); } hd = get_holiday_state(Y,M,i); if(hd>0 || hd==-1 && j>=5) XSetForeground(display, gc, base_color(CALENDAR_HDAY)); else XSetForeground(display, gc, base_color(CALENDAR_DAY)); snprintf(tmps, sizeof(tmps), "%u", i); dx = (i>=10)?10:6; if(dx>=CELL_SX) dx = 0; else dx = (CELL_SX-dx)/2; dy = 10; if(dy>=CELL_SY) dy = CELL_SY; else dy = (CELL_SY*4+dy*6)/10; XDrawImageString(display, win, gc, CAL_X+CELL_SX*j+dx, CAL_Y+CELL_SY*w+dy, tmps, strlen(tmps)); if(i==k) break; i++; j = (j+1)%7; if(!j) w++; } if(j<6) { j++; k = mdays(Y,M+1); i = 1; for( ; j<7; j++,i++) { hd = get_holiday_state(Y,M+1,i); if(hd>0 || hd==-1 && j>=5) XSetForeground(display, gc, base_color(CALENDAR_OTHERHDAY)); else XSetForeground(display, gc, base_color(CALENDAR_OTHERDAY)); snprintf(tmps, sizeof(tmps), "%u", i); dx = (i>=10)?10:6; if(dx>=CELL_SX) dx = 0; else dx = (CELL_SX-dx)/2; dy = 10; if(dy>=CELL_SY) dy = CELL_SY; else dy = (CELL_SY*4+dy*6)/10; XDrawImageString(display, win, gc, CAL_X+CELL_SX*j+dx, CAL_Y+CELL_SY*w+dy, tmps, strlen(tmps)); } } } static int get_cal_wday(void) { struct tm tm; time_t t; if(cal_Y<1970) { cal_Y = 1970; cal_Mpos = 0; } if(cal_Y>999999999) { cal_Y = 999999999; cal_Mpos = 1199; } if(cal_Mpos>1199) cal_Mpos = 1199; bzero(&tm, sizeof(tm)); tm.tm_year = cal_Y-1900; tm.tm_mon = cal_Mpos/100; tm.tm_mday = 1; tm.tm_hour = 12; tm.tm_min = 0; tm.tm_sec = 0; if((t = mktime(&tm))<0) return -1; if(!localtime_r(&t, &tm)) return -1; if(tm.tm_wday<0 || tm.tm_wday>7) return -1; cal_MW = (tm.tm_wday+6)%7; return 0; } static void taskbart_draw_interactive_calendar(unsigned cY, unsigned cM, unsigned cD) { static char const * mons[12] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; unsigned CAL_X, CAL_Y, CAL_SX, CAL_SY, CELL_SX, CELL_SY, MAXX; unsigned M, k, nw, p; int y0; char tmps[30]; unsigned pk, D, x, y, dx, dy; int hd; /* external geometry calc */ MAXX = cur_w; CAL_X = CALENDAR_X; CAL_Y = CALENDAR_Y-3; CELL_SX = CALENDAR_CELLSX; CELL_SY = CALENDAR_CELLSY; if(trc.present && trc.x0>MAXX/2 && trc.x0CAL_Y) MAXX = trc.x0; if(MAXX<40) { if(cal.present) { cal.present = 0; XFillRectangle(display, win, gc, cal.x0, cal.y0, cal.w, cal.h); } return; } while(CAL_X*2+CELL_SX*7>=MAXX) { if(CAL_X*4>=CELL_SX*3 || CAL_X*3>=CELL_SX*2 && CELL_SX<=10) CAL_X--; else CELL_SX--; } if(CELL_SY>=20) CELL_SY--; if(CELL_SY>=10) CELL_SY--; CAL_SX = CELL_SX*7; CAL_SY = CELL_SY*7+2; if(cal.present && cal.x0==CAL_X && cal.y0==CAL_Y && cal.w==CAL_SX && cal.h==CAL_SY && cal_redraw<2) return; if(!cal.present) fill_rect(base_color(CALENDAR_IBG), CAL_X, CAL_Y, CAL_SX, CAL_SY); else move_fill_rect(base_color(TASKBAR_BG), cal.x0, cal.y0, cal.w, cal.h, base_color(CALENDAR_IBG), CAL_X, CAL_Y, CAL_SX, CAL_SY); cal.x0 = CAL_X; cal.y0 = CAL_Y; cal.w = CAL_SX; cal.h = CAL_SY; cal.present = 1; /* drawing title */ hd = get_cal_wday(); M = cal_Mpos/100; XSetBackground(display, gc, base_color(CALENDAR_IBG)); XSetForeground(display, gc, base_color(TASKBAR_BUTTEXT_FOCUSED)); snprintf(tmps, sizeof(tmps), "%s %u", mons[M], cal_Y); dy = 10; if(dy>=CELL_SY) dy = CELL_SY; else dy = (CELL_SY*3+dy*7)/10; draw_string_centered(display, win, gc, CAL_X, CAL_Y+12, CAL_SX, tmps, strlen(tmps)); cal_py0 = (dy+CELL_SY)/2; cal_px0 = 0; cal_px1 = CAL_SX/2; cal_px2 = CAL_SX-CAL_SX/3; cal_px3 = CAL_SX-CAL_SX/6; CAL_Y += CELL_SY; /* calendar geometry calc */ if(hd<0) return; k = mdays(cal_Y, M+1); nw = (cal_MW+k+6)/7; /* number of calendar weeks per month */ p = cal_Mpos%100; if(p<=50) { if(cal_MW) { y0 = 250 - (nw-1)*p; /* cal_MW>0 means also nw>0 */ } else { y0 = 300 - nw*p; } } else { if((cal_MW+k)%7) { y0 = 250 - (nw-1)*p; /* cal_MW+k>0 means also nw>0 */ } else { y0 = 300 - nw*p; } } /* drawing calendar */ if((y0>=100 || y0>=0 && cal_MW) && (pk=mdays(cal_Y,M))) { /* previous month partially visible */ y = y0; if(cal_MW>=pk) { D=1; x=cal_MW-pk; } else if(cal_MW) { D = pk-(cal_MW-1); x = 0; } else if(pk>=7) { D = pk-6; x = 0; y -= 100; } else { D = 1; x = 7-pk; y -= 100; } assert(y<600); while(D>1 && y>=100) { assert(!x); y -= 100; if(D>7) D-=7; else { x+=8-D; D=1; } } while(D<=pk) { if(cal_Y==cY && M==cM && D==cD) { XSetForeground(display, gc, base_color(CALENDAR_CURDAYBG)); XFillRectangle(display, win, gc, CAL_X+CELL_SX*x, CAL_Y+CELL_SY*y/100, CELL_SX, CELL_SY); } hd = get_holiday_state(cal_Y,M,D); if(hd>0 || hd==-1 && x>=5) XSetForeground(display, gc, base_color(CALENDAR_OTHERHDAY)); else XSetForeground(display, gc, base_color(CALENDAR_OTHERDAY)); snprintf(tmps, sizeof(tmps), "%u", D); dx = (D>=10)?10:6; if(dx>=CELL_SX) dx = 0; else dx = (CELL_SX-dx)/2; dy = 10; if(dy>=CELL_SY) dy = CELL_SY; else dy = (CELL_SY*4+dy*6)/10; XDrawImageString(display, win, gc, CAL_X+CELL_SX*x+dx, CAL_Y+CELL_SY*y/100+dy, tmps, strlen(tmps)); D++; if(!(x=(x+1)%7)) y+=100; } assert((int)y==y0 && x==cal_MW); } { /* current month */ if(y0>=0) { D = 1; x = cal_MW; y = y0; } else { y = (99-y0)/100; D = 1+y*7-cal_MW; x = 0; y = y*100+y0; } assert(y<600); while(D<=k && y<=510) { if(cal_Y==cY && M+1==cM && D==cD) { XSetForeground(display, gc, base_color(CALENDAR_CURDAYBG)); XFillRectangle(display, win, gc, CAL_X+CELL_SX*x, CAL_Y+CELL_SY*y/100, CELL_SX, CELL_SY); } hd = get_holiday_state(cal_Y,M+1,D); if(hd>0 || hd==-1 && x>=5) XSetForeground(display, gc, base_color(CALENDAR_HDAY)); else XSetForeground(display, gc, base_color(CALENDAR_DAY)); snprintf(tmps, sizeof(tmps), "%u", D); dx = (D>=10)?10:6; if(dx>=CELL_SX) dx = 0; else dx = (CELL_SX-dx)/2; dy = 10; if(dy>=CELL_SY) dy = CELL_SY; else dy = (CELL_SY*4+dy*6)/10; XDrawImageString(display, win, gc, CAL_X+CELL_SX*x+dx, CAL_Y+CELL_SY*y/100+dy, tmps, strlen(tmps)); D++; if(!(x=(x+1)%7)) y+=100; } if(y>500) return; } { /* next month; using x,y values remaining from current month drawing */ D = 1; pk = mdays(cal_Y,M+2); assert(y<600); while(D<=pk && y<=500) { if(cal_Y==cY && M+2==cM && D==cD) { XSetForeground(display, gc, base_color(CALENDAR_CURDAYBG)); XFillRectangle(display, win, gc, CAL_X+CELL_SX*x, CAL_Y+CELL_SY*y/100, CELL_SX, CELL_SY); } hd = get_holiday_state(cal_Y,M+2,D); if(hd>0 || hd==-1 && x>=5) XSetForeground(display, gc, base_color(CALENDAR_OTHERHDAY)); else XSetForeground(display, gc, base_color(CALENDAR_OTHERDAY)); snprintf(tmps, sizeof(tmps), "%u", D); dx = (D>=10)?10:6; if(dx>=CELL_SX) dx = 0; else dx = (CELL_SX-dx)/2; dy = 10; if(dy>=CELL_SY) dy = CELL_SY; else dy = (CELL_SY*4+dy*6)/10; XDrawImageString(display, win, gc, CAL_X+CELL_SX*x+dx, CAL_Y+CELL_SY*y/100+dy, tmps, strlen(tmps)); D++; if(!(x=(x+1)%7)) y+=100; } } } static void taskbart_cal_scroll(int delta) { struct tm tm; if(!cal_active) { cal_active = 1; localtime_r(&now, &tm); cal_Y = tm.tm_year+1900; cal_Mpos = (tm.tm_mon%12)*100+50; } delta += cal_Mpos; while(delta<0) { delta += 1200; cal_Y--; } cal_Y += (delta/1200); cal_Mpos = (delta%1200); cal_redraw = 3; } static void taskbart_timer_update(void) { static char const * wdays[7] = {"Sun","Mon","Tue","Wed","Thu", "Fri", "Sat"}; static time_t oldnow = 0; static unsigned oldday = 0; struct tm tm; char dst[30]; unsigned CAL_X, CAL_Y, MAXX, dx; unsigned W, Y, M, D, h, m, s; if(!taskbar_created) return; if(now==oldnow && cal_redraw<3) return; oldnow = now; localtime_r(&now, &tm); W = tm.tm_wday%7; Y = tm.tm_year+1900; M = (tm.tm_mon%12)+1; D = tm.tm_mday%100; h = tm.tm_hour%100; m = tm.tm_min%100; s = tm.tm_sec%100; MAXX = cur_w; XSetForeground(display, gc, base_color(TASKBAR_BUTTEXT_FOCUSED)); snprintf(dst, sizeof(dst), "%s %4u-%02u-%02u %02u:%02u:%02u ", wdays[W], Y, M, D, h, m, s); CAL_X = CALENDAR_RAWX; CAL_Y = CALENDAR_RAWY; dx = text_width(gc, dst, strlen(dst)-1); if(bat.present && bat.x0>MAXX/2 && CAL_Y>bat.y0 && CAL_Y((unsigned)cur_w)/2 && CAL_Y>bl.y0 && CAL_Y=MAXX) { snprintf(dst, sizeof(dst), "%s %4u-%02u-%02u", wdays[W], Y, M, D); CAL_X = CALENDAR_RAWX; CAL_Y = CALENDAR_RAWY-5; dx = text_width(gc, dst, strlen(dst)); if(CAL_X>(MAXX-dx)/2) CAL_X = (MAXX-dx)/2; XDrawImageString(display, win, gc, CAL_X, CAL_Y, dst, strlen(dst)); snprintf(dst, sizeof(dst), "%02u:%02u:%02u", h, m, s); CAL_X = CALENDAR_RAWX; CAL_Y = CALENDAR_RAWY-7+TASKBAR_WINHEIGHT; dx = text_width(gc, dst, strlen(dst)); if(CAL_X>(MAXX-dx)/2) CAL_X = (MAXX-dx)/2; XDrawImageString(display, win, gc, CAL_X, CAL_Y, dst, strlen(dst)); } else { if(CAL_X>(MAXX-dx)/2) CAL_X = (MAXX-dx)/2; XDrawImageString(display, win, gc, CAL_X, CAL_Y, dst, strlen(dst)); } if(oldday!=D) { check_holidays_file(0); cal_redraw = 2; oldday = D; } if(!cal_redraw) return; if(!cal_active) taskbart_draw_passive_calendar(W, Y, M, D); else taskbart_draw_interactive_calendar(Y, M, D); XSetBackground(display, gc, base_color(TASKBAR_BG)); cal_redraw = 0; } #define BARCTL_ERR_BORDER RED #define BARCTL_ERR_BG LTRED #define BARCTL_ERR_TEXT BLACK #define BATTERY_BORDER WHITE #define BATTERY_BG LTGREEN #define BATTERY_BGC CYAN #define BATTERY_TEXT PINK #define BACKLIGHT_BORDER WHITE #define BACKLIGHT_BG YELLOW #define BACKLIGHT_TEXT PINK #define BARCTL_HEIGHT 13 #define BARCTL_TXTOFS 11 static void ac_power_icon_10x10(unsigned x0, unsigned y0, unsigned long ltc, unsigned long dkc) { /* try this: lt=6666FF, dk=2244FF */ /* 10x10 area, 8x8 picture */ XSetForeground(display, gc, ltc); XFillRectangle(display, win, gc, x0+2, y0+1, 2, 3); XFillRectangle(display, win, gc, x0+6, y0+1, 2, 3); XSetForeground(display, gc, dkc); XFillRectangle(display, win, gc, x0+1, y0+3, 8, 3); XFillRectangle(display, win, gc, x0+2, y0+6, 6, 1); XFillRectangle(display, win, gc, x0+4, y0+7, 2, 2); } static void ac_power_icon_10x12(unsigned x0, unsigned y0, unsigned long ltc, unsigned long dkc) { /* 10x12 area, 8x10 picture */ XSetForeground(display, gc, ltc); XFillRectangle(display, win, gc, x0+2, y0+1, 2, 3); XFillRectangle(display, win, gc, x0+6, y0+1, 2, 3); XSetForeground(display, gc, dkc); XFillRectangle(display, win, gc, x0+1, y0+4, 8, 3); XFillRectangle(display, win, gc, x0+2, y0+7, 6, 1); XFillRectangle(display, win, gc, x0+4, y0+8, 2, 3); } static void backlight_icon_11x11(unsigned x0, unsigned y0, unsigned long ltc, unsigned long dkc) { #if 1 /* 11x11 area, 9x9 picture */ XSetForeground(display, gc, ltc); XDrawArc(display, win, gc, x0+3, y0+3, 4, 4, 0, 360*64); XSetForeground(display, gc, dkc); XDrawLine(display, win, gc, x0+1, y0+5, x0+1, y0+5); XDrawLine(display, win, gc, x0+5, y0+9, x0+5, y0+9); XDrawLine(display, win, gc, x0+5, y0+1, x0+5, y0+1); XDrawLine(display, win, gc, x0+9, y0+5, x0+9, y0+5); XDrawLine(display, win, gc, x0+2, y0+2, x0+2, y0+2); XDrawLine(display, win, gc, x0+2, y0+8, x0+2, y0+8); XDrawLine(display, win, gc, x0+8, y0+8, x0+8, y0+8); XDrawLine(display, win, gc, x0+8, y0+2, x0+8, y0+2); #else XSetForeground(display, gc, ltc); XDrawArc(display, win, gc, x0+4, y0+4, 2, 2, 0, 360*64); XSetForeground(display, gc, dkc); XDrawLine(display, win, gc, x0+1, y0+5, x0+2, y0+5); XDrawLine(display, win, gc, x0+5, y0+8, x0+5, y0+9); XDrawLine(display, win, gc, x0+5, y0+1, x0+5, y0+2); XDrawLine(display, win, gc, x0+8, y0+5, x0+9, y0+5); XDrawLine(display, win, gc, x0+2, y0+2, x0+3, y0+3); XDrawLine(display, win, gc, x0+2, y0+8, x0+3, y0+7); XDrawLine(display, win, gc, x0+7, y0+7, x0+8, y0+8); XDrawLine(display, win, gc, x0+7, y0+3, x0+8, y0+2); #endif } static int last_bl, last_pct, last_bat_err, last_charging; static void taskbart_status_update(int reopen, int force_redraw) { static time_t oldnow; unsigned W, x0, y0, w, hw, pctt; char st[30]; int r; fsysd_pwr pwr; if(reopen) fsysd_reopen(); else if(!force_redraw && bl.present!=2 && oldnow==now) return; oldnow = now; if(cur_w<=40) return; W = cur_w; x0 = (W>=42)?(W-42):0; w = W-x0-1; y0 = 20; bat.x0 = x0; bat.y0 = y0; bat.w = w; bat.h = BARCTL_HEIGHT; if((r = fsysd_get_pwr(&pwr))>0) { if(!bat.present) { last_pct = last_bat_err = last_charging = -2; } if(pwr.is_ac>0) { if(bat.present<2 || force_redraw) { ac_power_icon_10x12(x0,y0+1,base_color(WHITE),base_color(CYAN)); } bat.present = 2; } else { if(bat.present>1 || force_redraw) { XSetForeground(display, gc, base_color(TASKBAR_BG)); XFillRectangle(display, win, gc, x0, y0, 11, BARCTL_HEIGHT); } bat.present = 1; } if(pwr.bat_err) { if(!last_bat_err || force_redraw) { XSetForeground(display, gc, base_color(BARCTL_ERR_BORDER)); XDrawRectangle(display, win, gc, x0+11, y0, w-13, BARCTL_HEIGHT-1); XSetForeground(display, gc, base_color(BARCTL_ERR_BG)); XFillRectangle(display, win, gc, x0+12, y0+1, w-14, BARCTL_HEIGHT-2); XSetForeground(display, gc, base_color(BARCTL_ERR_TEXT)); XDrawString(display, win, gc, x0+13, y0+BARCTL_TXTOFS, "ERR", 3); } last_bat_err = 1; } else if(force_redraw || last_bat_err || last_pct!=pwr.charge_pctt || last_charging!=pwr.is_charging) { last_bat_err = 0; last_pct = pwr.charge_pctt; last_charging = pwr.is_charging; XSetForeground(display, gc, base_color(BATTERY_BORDER)); XDrawRectangle(display, win, gc, x0+11, y0, w-13, BARCTL_HEIGHT-1); hw = (w-14); pctt = pwr.charge_pctt; if(pctt>1000) pctt=1000; hw = hw*pctt/1000; if(hw) { XSetForeground(display, gc, base_color(BATTERY_BG)); XFillRectangle(display, win, gc, x0+12, y0+1, hw, BARCTL_HEIGHT-2); } if(hw0)?BATTERY_BGC:TASKBAR_BG)); XFillRectangle(display, win, gc, x0+12+hw, y0+1, w-14-hw, BARCTL_HEIGHT-2); } snprintf(st, sizeof(st), "%u%%", pctt/10); XSetForeground(display, gc, base_color(BATTERY_TEXT)); XDrawString(display, win, gc, x0+13, y0+BARCTL_TXTOFS, st, strlen(st)); } } else if(r<=-2) { /* r==-1 means file missing on unreadable, r==-2 means malformed contents */ if(bat.present>1 || force_redraw) { XSetForeground(display, gc, base_color(TASKBAR_BG)); XFillRectangle(display, win, gc, x0, y0, 11, BARCTL_HEIGHT); } bat.present = 1; if(!last_bat_err || force_redraw) { XSetForeground(display, gc, base_color(BARCTL_ERR_BORDER)); XDrawRectangle(display, win, gc, x0+11, y0, w-13, BARCTL_HEIGHT-1); XSetForeground(display, gc, base_color(BARCTL_ERR_BG)); XFillRectangle(display, win, gc, x0+12, y0+1, w-14, BARCTL_HEIGHT-2); XSetForeground(display, gc, base_color(BARCTL_ERR_TEXT)); XDrawString(display, win, gc, x0+13, y0+BARCTL_TXTOFS, "ERR", 3); } last_bat_err = 1; } else if(bat.present || force_redraw) { bat.present = 0; XSetForeground(display, gc, base_color(TASKBAR_BG)); XFillRectangle(display, win, gc, x0, y0, w, BARCTL_HEIGHT); } y0 = 37; bl.x0 = x0; bl.y0 = y0; bl.w = w; bl.h = BARCTL_HEIGHT; if((r=fsysd_get_bl())>=0) { if(force_redraw || last_bl!=r || bl.present!=1) { /* bl.present==2 used as redraw marker */ XSetForeground(display, gc, base_color(BACKLIGHT_BORDER)); XDrawRectangle(display, win, gc, x0+11, y0, w-13, BARCTL_HEIGHT-1); hw = (w-14); pctt = r; if(pctt>100) pctt=100; hw = hw*pctt/100; if(hw) { XSetForeground(display, gc, base_color(BACKLIGHT_BG)); XFillRectangle(display, win, gc, x0+12, y0+1, hw, BARCTL_HEIGHT-2); } if(hwcal.y0) cal_redraw = 2; if(y1<60 && y2>20) taskbart_status_update(0, 1); /* TODO: coords config */ } extern void taskbart_mouse_click(int x, int y) { unsigned xx, yy; if(x<0 || y<0) return; xx = x; yy = y; if(bl.present && xx>bl.x0 && xx-bl.x0bl.y0 && yy-bl.y0cal.x0 && xx-cal.x0cal.y0 && yy-cal.y0bl.x0 && xx-bl.x0bl.y0 && yy-bl.y0cal.x0 && xx-cal.x0cal.y0 && yy-cal.y0cal_py0) taskbart_cal_scroll(-delta*5); else if(xx-cal.x0>=cal_px0 && xx-cal.x0<=cal_px1) taskbart_cal_scroll(-delta*100); else if(xx-cal.x0>=cal_px2 && xx-cal.x0<=cal_px3) taskbart_cal_scroll(-delta*1200); } } extern void taskbart_brightness(int delta) { int new_bl; if(bl.present && last_bl>=0 && last_bl<=100) { new_bl = last_bl+delta; if(new_bl<0) new_bl = 0; if(new_bl>100) new_bl = 100; if(new_bl!=last_bl) { fsysd_set_bl(new_bl); bl.present = 2; taskbart_status_update(0, 0); } } } /*********************************************************************************************/ /****************************************** systray ******************************************/ /*********************************************************************************************/ typedef struct { int n; WInfo *p[MAX_WINDOWS]; } spcwins; static spcwins tray, widgets; extern WInfo * tray_add_winfo(Window window, char const *wcl) { int n; WInfo * wi; assert(tray.n<=MAX_WINDOWS); if(tray.n==MAX_WINDOWS) return NULL; if(!(wi = WInfo_new(window))) return NULL; tray.p[n=tray.n++] = wi; wi->cat = 50000; wi->cpos = n; strncpy(wi->fwm_wclass, wcl, sizeof(wi->fwm_wclass)-1); XSetWindowBorderWidth(display, wi->window, 0); wi->w = -1; /* ensure that repositioning request will be sent to X server */ tray_recalc_coords(); return wi; } extern void tray_del_winfo(WInfo * wi) { int n, m; WInfo *wi2; Window window; window = wi->window; assert(tray.n<=MAX_WINDOWS); assert(wi->cat==50000); n = wi->cpos; assert(nwdb.list); tray.n--; if(ncat==50000 && wi2->cpos==(unsigned)(m+1)); (tray.p[m] = wi2)->cpos = m; } } tray_recalc_coords(); } static void trayicon_fail(WInfo *wi) { assert(wi->fwm_wclass[0]==WCLASS_TRAYICON || wi->fwm_wclass[0]==WCLASS_TRAYWIDE); if(wi->x0 || wi->y0 || wi->w || wi->h) { wi->h = wi->w = wi->y0 = wi->x0 = 0; place_window_below(display, wi->window, bg_window); } } static void trayicon_set_coords(WInfo *wi, int x0, int y0) { XWindowChanges chg; int w; assert(wi->fwm_wclass[0]==WCLASS_TRAYICON || wi->fwm_wclass[0]==WCLASS_TRAYWIDE); w = (wi->fwm_wclass[0]==WCLASS_TRAYWIDE)?40:16; if(wi->x0!=x0 || wi->y0!=y0 || wi->w!=w || wi->h!=16) { chg.x = wi->x0 = x0; chg.y = wi->y0 = y0; chg.width = wi->w = w; chg.height = wi->h = 16; chg.border_width = 0; chg.sibling = win; chg.stack_mode = Above; XConfigureWindow(display, wi->window, CWX | CWY | CWWidth | CWHeight | CWBorderWidth | CWSibling | CWStackMode, &chg); } } extern void tray_recalc_coords(void) { int n; unsigned int Wx, Wy, x, y; unsigned int x0, y0, w, h, ww, hh, wt; /* tray zone geometry, pixels and icons */ unsigned int num_icons, num_wide, count; unsigned int W; WInfo *wi; if(cur_w<20) goto all_fail; W = cur_w; y0 = 54; if(y0>=TASKBAR_TOPAREA-1) goto all_fail; h = TASKBAR_TOPAREA-y0-1; hh = h/17; if(hh<1) goto all_fail; num_wide = num_icons = 0; for(n=0; nfwm_wclass[0]==WCLASS_TRAYICON) num_icons++; if(num_wide<100 && wi->fwm_wclass[0]==WCLASS_TRAYWIDE) num_wide++; } if(!num_icons && !num_wide) { if(trc.present) { trc.present=0; if(cal_redraw<1) cal_redraw = 1; } return; } if(!num_wide) ww = (num_icons-1)/hh+1; else { ww = (num_wide*3+num_icons-1)/hh+1; wt = ((num_wide-1)/hh+1)*3; if(ww(W-2)/17) ww = (W-2)/17; w = ww*17; x0 = W-w; Wy = Wx = 0; if(!trc.present || trc.x0!=x0 || trc.y0!=y0 || trc.w!=w || trc.h!=h) { trc.present = 1; trc.x0 = x0; trc.y0 = y0; trc.w = w; trc.h = h; if(cal_redraw<1) cal_redraw = 1; } for(count=0,n=0; count=3; n++) if((wi=tray.p[n])->fwm_wclass[0]==WCLASS_TRAYWIDE) { count++; trayicon_set_coords(wi, taskbar_x0+x0+Wx*17+10, taskbar_y0+y0+Wy*17); Wy++; if(Wy>=hh) { Wy=0; Wx+=3; } } for( ; nfwm_wclass[0]==WCLASS_TRAYWIDE) trayicon_fail(wi); if(ww-Wx<1) { y=hh; x=ww; } else if(Wy && ww-Wx>3) { y = 0; x = Wx+3; } else { y = Wy; x = Wx; } for(count=0,n=0; countfwm_wclass[0]==WCLASS_TRAYICON) { count++; trayicon_set_coords(wi, taskbar_x0+x0+x*17, taskbar_y0+y0+y*17); x++; if(x>=ww) { y++; x = Wx; if(yfwm_wclass[0]==WCLASS_TRAYICON) trayicon_fail(wi); return; all_fail: for(n=0; ncat = 50001; wi->cpos = n; strncpy(wi->fwm_wclass, wcl, sizeof(wi->fwm_wclass)-1); wi->w = -1; /* ensure that repositioning request will be sent to X server */ widgets_recalc_coords(); return wi; } extern void widgets_del_winfo(WInfo * wi) { int n, m; WInfo *wi2; Window window; window = wi->window; assert(widgets.n<=MAX_WINDOWS); assert(wi->cat==50001); n = wi->cpos; assert(nwdb.list); widgets.n--; if(ncat==50001 && wi2->cpos==(unsigned)(m+1)); (widgets.p[m] = wi2)->cpos = m; } } widgets_recalc_coords(); } static void widget_fail(WInfo *wi) { assert(wi->fwm_wclass[0]==WCLASS_WIDGET); if(wi->x0 || wi->y0 || wi->w || wi->h) { wi->h = wi->w = wi->y0 = wi->x0 = 0; place_window_below(display, wi->window, bg_window); } } static int widget_set_coords(WInfo *wi, int x0, int y0, int w, int h, int bw) { XWindowAttributes wa; XWindowChanges chg; int first_call; assert(wi->fwm_wclass[0]==WCLASS_WIDGET); if(wi->w<0 || wi->h<0 || !wi->w && !wi->h) { first_call = 1; /* place previously failed widgets here too; TODO: we may remember their width/height */ if(w==0 || w==-1 || h==0 || h==-1) { /* still unconfigured window and we need its "default" width/height */ if(!XGetWindowAttributes(display, wi->window, &wa)) return -1; wi->w = (wa.width>=0)?wa.width:0; wi->h = (wa.height>=0)?wa.height:0; } } else { first_call = 0; } if(w==0) w = wi->w; if(w==-1) w = -wi->w; if(h==0) h = wi->h; if(h==-1) h = -wi->h; if(w<-10000) w = -10000; if(h<-10000) h = -10000; if(w>10000) w = 10000; if(h>10000) h = 10000; /* TODO: allow application to resize its widget window and recalc all sizes after that */ if(w<0) { if(x0>w) x0 += (w+1); /* prevent overflows and do not care about anyway invisible windows */ x0 -= bw*2; w = -w; } if(h<0) { if(y0>h) y0 += (h+1); /* prevent overflows and do not care about anyway invisible windows */ y0 -= bw*2; h = -h; } if(first_call || wi->x0!=x0 || wi->y0!=y0 || wi->w!=w || wi->h!=h) { /* TODO: we should align all coords with possible border_width or disallow it at all */ chg.x = wi->x0 = x0; chg.y = wi->y0 = y0; chg.width = wi->w = w; chg.height = wi->h = h; chg.border_width = bw; chg.sibling = win; chg.stack_mode = Above; XConfigureWindow(display, wi->window, CWX | CWY | CWWidth | CWHeight | ((bw>=0)?CWBorderWidth:0) | CWSibling | CWStackMode, &chg); } return 0; } /* TODO: support 'float' widgets */ extern void widgets_recalc_coords(void) { int n; widget_conf const *wc; int x, y, w, h, bw; WInfo *wi; static widget_conf const def_wc = { .type=w_float, .xbase=w_none, .bw=-1 }; for(n=0; nfwm_wclass[0]==WCLASS_WIDGET) { wc = get_widget_conf_by_name(wi->fwm_wclass+1); if(wc && wc->type==w_fixed) { x = wc->xoffs; y = wc->yoffs; switch(wc->xbase) { case w_screen: if(x<0) x += sw; if(x<0) x = 0; if(x>=sw) x = sw-1; break; case w_viewport: if(x<0) x += cur_x0; if(x<0) x = 0; if(x>=cur_x0) x = cur_x0-1; break; case w_taskbar: if(x>=0) x += cur_x0; else x += sw; if(x=sw) x = sw-1; break; } switch(wc->ybase) { case w_screen: case w_viewport: case w_taskbar: if(y<0) y += sh; if(y<0) y = 0; if(y>=sh) y = sh-1; break; } if(widget_set_coords(wi, x, y, wc->w, wc->h, wc->bw)<0) widget_fail(wi); } else { if(!wc) wc = &def_wc; /* TODO: tiling algo for all widgets; also allow to move them manually after that */ widget_fail(wi); } } }