#include #include #include #include #include "xte.h" #include "builtins.h" #include "blocktype.h" typedef struct priv PRIV; typedef struct sb SB; typedef struct kind KIND; struct kind { const char *name; int namelen; BLOCKDIMS d; int x_name; int x_val; int w_val; int x_out; } ; #define KINDINIT(n) { .name = (n), .d = { .w = -1 } } struct priv { unsigned int flags; #define PF_CTL_FREQ 0x00000001 #define PF_CTL_DUTY 0x00000002 double freq; double freq2; double duty; double duty2; } ; struct sb { void (*cont)(void *, void *); void *contarg; PRIV p; } ; static int w_freq; static int w_duty; static int w_fv; static int w_dv; static int w_fdv; static int w_out = -1; static int w_dash; static Pixmap pm_outs = None; static int widthof(const char *s, int l) { XCharStruct cs; XTextExtents(font,s,l,XTE_JUNK,&cs); return(cs.width); } static BLOCKDIMS size(KIND *k, unsigned int flags) { int lw; int w; int w_name; if (k->d.w < 0) { k->namelen = strlen(k->name); w_name = widthof(k->name,k->namelen); if (w_out < 0) { w_freq = widthof("Freq",4); w_duty = widthof("Duty",4); w_dash = widthof("-",1); w_fv = widthof("0.00kHz",7); w = widthof("0.00mHz",7); if (w > w_fv) w_fv = w; w = widthof("0.00µHz",7); if (w > w_fv) w_fv = w; w = widthof("0.00nHz",7); if (w > w_fv) w_fv = w; w = widthof("epsilon",7); if (w > w_fv) w_fv = w; w = widthof("GHz",7); if (w > w_fv) w_fv = w; w_dv = widthof("00.0%",5); w_fdv = (w_fv > w_dv) ? w_fv : w_dv; w_out = 3 * font_space; if (w_out < font_baselineskip) w_out = font_baselineskip; } switch (flags & (PF_CTL_FREQ|PF_CTL_DUTY)) { case 0: lw = font_space; w = w_fdv; if (w_name > w) w = w_name; break; case PF_CTL_FREQ: lw = w_freq + (2 * font_space); w = (2 * w_fv) + (2 * font_space) + w_dash; if (w_dv > w) w = w_dv; if (w_name > w) w = w_name; break; case PF_CTL_DUTY: lw = w_duty + (2 * font_space); w = (2 * w_dv) + (2 * font_space) + w_dash; if (w_fv > w) w = w_fv; if (w_name > w) w = w_name; break; case PF_CTL_FREQ | PF_CTL_DUTY: lw = ((w_freq > w_duty) ? w_freq : w_duty) + (2 * font_space); w = (2 * w_fdv) + (2 * font_space) + w_dash; if (w_name > w) w = w_name; break; } k->x_val = lw; k->w_val = w; w += lw; k->d.w = w + w_out + (2 * font_space); k->d.h = 4 * font_baselineskip; k->x_name = k->x_val + ((k->w_val - w_name) / 2); k->x_out = k->d.w - w_out - font_space; } return(k->d); } static void create_pm_outs(Drawable d) { int h; XPoint *tpts; int x; int y; tpts = malloc(((w_out>6)?w_out:6)*sizeof(XPoint)); h = 3 * font_baselineskip; pm_outs = XCreatePixmap(disp,d,w_out,h,1); XFillRectangle(disp,pm_outs,gc_1bpp_0,0,0,w_out,h); for (x=0;x font_baselineskip-2) y = font_baselineskip - 2; tpts[x].x = x; tpts[x].y = y; } XDrawPoints(disp,pm_outs,gc_1bpp_1,&tpts[0],w_out,CoordModeOrigin); tpts[0].x = 0; tpts[0].y = h / 2; tpts[1].x = w_out / 4; tpts[1].y = font_baselineskip + 1; tpts[2].x = (3 * w_out) / 4; tpts[2].y = (2 * font_baselineskip) - 2; tpts[3].x = w_out - 1; tpts[3].y = h / 2; XDrawLines(disp,pm_outs,gc_1bpp_1,&tpts[0],4,CoordModeOrigin); tpts[0].x = 0; tpts[0].y = (5 * font_baselineskip) / 2; tpts[1].x = 0; tpts[1].y = (2 * font_baselineskip) + 1; tpts[2].x = w_out / 2; tpts[2].y = (2 * font_baselineskip) + 1; tpts[3].x = w_out / 2; tpts[3].y = (3 * font_baselineskip) - 2; tpts[4].x = w_out - 1; tpts[4].y = (3 * font_baselineskip) - 2; tpts[5].x = w_out - 1; tpts[5].y = tpts[0].y; XDrawLines(disp,pm_outs,gc_1bpp_1,&tpts[0],6,CoordModeOrigin); free(tpts); } static int cvt_freq(double freq, char *buf) { int l; long long int ifreq; int i; unsigned char suf; if (freq < 0) panic(); do <"no0"> { do <"0"> { if (freq < 1) { ifreq = (freq * 1e12) + .5; // ifreq = freq in pHz if (ifreq < 1) { strcpy(buf,"epsilon"); return(7); } if (ifreq < 10) { return(sprintf(buf,".00%dnHz",(int)ifreq)); } if (ifreq < 100) { l = sprintf(buf,".0%d",(int)ifreq); while (buf[l-1] == '0') l --; strcpy(&buf[l],"nHz"); return(l+3); } if (ifreq < 1000) // <= .999nHz { l = sprintf(buf,".%d",(int)ifreq); while (buf[l-1] == '0') l --; strcpy(&buf[l],"nHz"); return(l+3); } if (ifreq < 10000) ifreq += 5; if (ifreq < 10000) // <= 9.99nHz { i = (int)ifreq / 10; suf = 'n'; l = sprintf(buf,"%d.%02d",i/100,i%100); break <"0">; } if (ifreq < 100000) ifreq += 50; if (ifreq < 100000) // <= 99.9nHz { i = (int)ifreq / 100; suf = 'n'; l = sprintf(buf,"%d.%d",i/10,i%10); break <"0">; } if (ifreq < 1000000) ifreq += 500; if (ifreq < 1000000) // <= 999nHz { i = (int)ifreq / 1000; suf = 'n'; l = sprintf(buf,"%d",i); break <"no0">; } if (ifreq < 10000000) // <= 9999nHz (9.999µHz) { i = (int)ifreq / 1000; suf = 'n'; l = sprintf(buf,"%d",i); break <"no0">; } if (ifreq < 100000000LL) ifreq += 50000; if (ifreq < 100000000LL) // <= 99.9µHz { i = ifreq / 100000LL; suf = 'µ'; l = sprintf(buf,"%d.%d",i/10,i%10); break <"0">; } if (ifreq < 1000000000LL) ifreq += 500000LL; if (ifreq < 1000000000LL) // <= 999µHz { i = ifreq / 1000000LL; suf = 'µ'; l = sprintf(buf,"%d",i); break <"no0">; } if (ifreq < 10000000000LL) // <= 9999µHz (9.999mHz) { i = ifreq / 1000000LL; suf = 'µ'; l = sprintf(buf,"%d",i); break <"no0">; } if (ifreq < 100000000000LL) ifreq += 50000000LL; if (ifreq < 100000000000LL) // <= 99.99mHz { i = ifreq / 100000000LL; suf = 'm'; l = sprintf(buf,"%d.%d",i/10,i%10); break <"0">; } ifreq += 500000000LL; i = ifreq / 1000000000LL; suf = 'm'; l = sprintf(buf,"%d",i); break <"no0">; } if (freq >= 1e9) { strcpy(buf,"GHz"); return(3); } ifreq = (freq * 1000) + .5; // ifreq = freq in mHz if (ifreq < 10000LL) ifreq += 5; if (ifreq < 10000LL) // <= 9.99Hz { i = (int)ifreq / 10; suf = '\0'; l = sprintf(buf,"%d.%02d",i/100,i%100); break <"0">; } if (ifreq < 100000LL) ifreq += 50; if (ifreq < 100000LL) // <= 99.9Hz { i = (int)ifreq / 100; suf = '\0'; l = sprintf(buf,"%d.%d",i/10,i%10); break <"0">; } if (ifreq < 1000000LL) ifreq += 500; if (ifreq < 1000000LL) // <= 999Hz { i = (int)ifreq / 1000; suf = '\0'; l = sprintf(buf,"%d",i); break <"no0">; } if (ifreq < 10000000LL) // <= 9999Hz (9.999kHz) { i = (int)ifreq / 1000; suf = '\0'; l = sprintf(buf,"%d",i); break <"no0">; } if (ifreq < 100000000LL) ifreq += 50000; if (ifreq < 100000000LL) // <= 99.9kHz { i = ifreq / 100000LL; suf = 'k'; l = sprintf(buf,"%d.%d",i/10,i%10); break <"0">; } if (ifreq < 1000000000LL) ifreq += 500000LL; if (ifreq < 1000000000LL) // <= 999kHz { i = ifreq / 1000000LL; suf = 'k'; l = sprintf(buf,"%d",i); break <"0">; } if (ifreq < 10000000000LL) // <= 9999kHz { i = ifreq / 1000000LL; suf = 'k'; l = sprintf(buf,"%d",i); break <"0">; } if (ifreq < 100000000000LL) ifreq += 50000000LL; if (ifreq < 100000000000LL) // <= 99.9MHz { i = ifreq / 100000000LL; suf = 'M'; l = sprintf(buf,"%d.%d",i/10,i%10); break <"0">; } if (ifreq < 1000000000000LL) ifreq += 500000000LL; if (ifreq < 1000000000000LL) // <= 999MHz { i = ifreq / 1000000000LL; suf = 'M'; l = sprintf(buf,"%d",i); break <"0">; } strcpy(buf,"GHz"); return(3); } while (0); while (buf[l-1] == '0') l --; if (buf[l-1] == '.') l --; } while (0); if (suf) buf[l++] = suf; buf[l++] = 'H'; buf[l++] = 'z'; return(l); } static int cvt_duty(double duty, char *buf) { int l; if ((duty < 0) || (duty > 100)) panic(); l = sprintf(buf,"%.1f",duty); if (l == 5) { if (strcmp(buf,"100.0")) panic(); l -= 2; } else { if (buf[l-1] == '0') l --; if (buf[l-1] == '.') l --; } buf[l++] = '%'; return(l); } static void render(void *pv, KIND *k, Drawable d, GC gc_bg, GC gc_fg) { PRIV *p; int y; char s1[16]; char s2[16]; char s[40]; int l1; int l2; int l; XCharStruct cs; p = pv; XFillRectangle(disp,d,gc_bg,0,0,k->d.w,k->d.h); XDrawLine(disp,d,gc_fg,0,0,k->d.w-2,0); XDrawLine(disp,d,gc_fg,k->d.w-1,0,k->d.w-1,k->d.h-2); XDrawLine(disp,d,gc_fg,k->d.w-1,k->d.h-1,1,k->d.h-1); XDrawLine(disp,d,gc_fg,0,k->d.h-1,0,1); y = font_baseline + (font_baselineskip / 2); XDrawString(disp,d,gc_fg,k->x_name,y,k->name,k->namelen); y += font_baselineskip; if (p->flags & PF_CTL_FREQ) { l1 = cvt_freq(p->freq,&s1[0]); l2 = cvt_freq(p->freq2,&s2[0]); l = sprintf(&s[0],"%s - %s",&s1[0],&s2[0]); } else { l = cvt_freq(p->freq,&s[0]); } XTextExtents(font,&s[0],l,XTE_JUNK,&cs); XDrawString(disp,d,gc_fg,k->x_val+((k->w_val-cs.width)/2),y,&s[0],l); y += font_baselineskip; if (p->flags & PF_CTL_DUTY) { l1 = cvt_duty(p->duty,&s1[0]); l2 = cvt_duty(p->duty2,&s2[0]); l = sprintf(&s[0],"%s - %s",&s1[0],&s2[0]); } else { l = cvt_duty(p->duty,&s[0]); } XTextExtents(font,&s[0],l,XTE_JUNK,&cs); XDrawString(disp,d,gc_fg,k->x_val+((k->w_val-cs.width)/2),y,&s[0],l); switch (p->flags & (PF_CTL_FREQ|PF_CTL_DUTY)) { case 0: break; case PF_CTL_FREQ: XDrawString(disp,d,gc_fg,font_space,font_baseline+((font_baselineskip*3)/2),"Freq",4); break; case PF_CTL_DUTY: XDrawString(disp,d,gc_fg,font_space,font_baseline+((font_baselineskip*3)/2),"Duty",4); break; case PF_CTL_FREQ | PF_CTL_DUTY: XDrawString(disp,d,gc_fg,font_space,font_baseline+(font_baselineskip/2),"Freq",4); XDrawString(disp,d,gc_fg,font_space,font_baseline+((font_baselineskip*5)/2),"Duty",4); break; } if (pm_outs == None) create_pm_outs(d); XCopyPlane(disp,pm_outs,d,gc_fg,0,0,w_out,font_baselineskip*3,k->x_out,font_baselineskip/2,1); } static KIND kind_osc = KINDINIT("Osc"); static BLOCKDIMS size_osc(void) { return(size(&kind_osc,0)); } static void setup_osc_cont2(int ok, void *sbv) { SB sb; PRIV *p; sb = *(SB *)sbv; free(sbv); if (ok) { p = malloc(sizeof(PRIV)); *p = sb.p; } else { p = 0; } (*sb.cont)(p,sb.contarg); } static void setup_osc_cont1(int ok, void *sbv) { SB sb; if (ok) { setup_getarg("Duty cycle",&setup_osc_cont2,sbv,&((SB *)sbv)->p.duty,PAR_FRANGE,0.,100.); } else { sb = *(SB *)sbv; free(sbv); (*sb.cont)(0,sb.contarg); } } static void setup_osc(void (*cont)(void *, void *), void *arg) { SB *sb; sb = malloc(sizeof(SB)); sb->cont = cont; sb->contarg = arg; sb->p.flags = 0; setup_getarg("Frequency",&setup_osc_cont1,sb,&sb->p.freq,PAR_FREQ); } static void destroy_osc(void *pv) { free(pv); } static void render_osc(void *pv, Drawable d, GC gc_bg, GC gc_fg) { render(pv,&kind_osc,d,gc_bg,gc_fg); } #if 0 static const char *ins[] = { "Freq", "Duty" }; #endif static const char *outs[] = { "Sin", "Tri", "Sq" }; static BLOCKPARAM params_osc[] = { { .name = "Freq", .type = PAR_FREQ }, { .name = "Duty%", .type = PAR_FRANGE, .u = { .frange = { .min = 0, .max = 100 } } } }; BLOCKTYPE block_osc = { .name = "Osc", .n_in = 0, .ins = 0, .n_out = 3, .outs = &outs[0], .n_param = 2, .params = ¶ms_osc[0], .compute_size = &size_osc, .setup = &setup_osc, .destroy = &destroy_osc, .render = &render_osc }; #if 0 static BLOCKPARAM params_vco[] = { { .name = "Freq1", .type = PAR_FREQ }, { .name = "Freq2", .type = PAR_FREQ }, { .name = "Duty%", .type = PAR_FRANGE, .u = { .frange = { .min = 0, .max = 100 } } } }; BLOCKTYPE block_vco = { .name = "VCO", .n_in = 1, .ins = &ins[0], .n_out = 3, .outs = &outs[0], .n_param = 3, .params = ¶ms_vco[0], .compute_size = &size_vco, .setup = &setup_vco, .destroy = &destroy_vco, .render = &render_vco }; static BLOCKPARAM params_ddcosc[] = { { .name = "Freq", .type = PAR_FREQ }, { .name = "Duty%1", .type = PAR_FRANGE, .u = { .frange = { .min = 0, .max = 100 } } }, { .name = "Duty%2", .type = PAR_FRANGE, .u = { .frange = { .min = 0, .max = 100 } } } }; BLOCKTYPE block_ddcosc = { .name = "DDCOsc", .n_in = 1, .ins = &ins[1], .n_out = 3, .outs = &outs[0], .n_param = 3, .params = ¶ms_ddcosc[0], .compute_size = &size_ddcosc, .setup = &setup_ddcosc, .destroy = &destroy_ddcosc, .render = &render_ddcosc }; static BLOCKPARAM params_genosc[] = { { .name = "Freq1", .type = PAR_FREQ }, { .name = "Freq2", .type = PAR_FREQ }, { .name = "Duty%1", .type = PAR_FRANGE, .u = { .frange = { .min = 0, .max = 100 } } }, { .name = "Duty%2", .type = PAR_FRANGE, .u = { .frange = { .min = 0, .max = 100 } } } }; BLOCKTYPE block_genosc = { .name = "GenOsc", .n_in = 2, .ins = &ins[0], .n_out = 3, .outs = &outs[0], .n_param = 4, .params = ¶ms_genosc[0], .compute_size = &size_genosc, .setup = &setup_genosc, .destroy = &destroy_genosc, .render = &render_genosc }; #endif