From 619bf2d74ef3e5431cb6057698f324590368122b Mon Sep 17 00:00:00 2001 From: Miller Puckette Date: Thu, 22 May 2008 18:25:27 +0000 Subject: 0.42-0 test 01. No real work yet, just bug fixes and updates. svn path=/trunk/; revision=9867 --- pd/src/d_fftroutine.c | 22 +++--- pd/src/d_math.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++- pd/src/g_array.c | 5 +- pd/src/g_canvas.c | 186 ++++++++------------------------------------- pd/src/g_canvas.h | 6 +- pd/src/g_editor.c | 205 ++++++++++++++++++++++++++++++++++++++++++++------ pd/src/g_graph.c | 10 +-- pd/src/g_template.c | 12 +-- pd/src/g_text.c | 16 +--- pd/src/m_class.c | 20 ++++- pd/src/m_pd.h | 25 +++--- pd/src/notes.txt | 4 +- pd/src/s_main.c | 3 +- pd/src/s_path.c | 24 ++++-- pd/src/s_stuff.h | 1 + pd/src/x_arithmetic.c | 20 ++++- pd/src/x_time.c | 16 ++-- 17 files changed, 523 insertions(+), 247 deletions(-) (limited to 'pd/src') diff --git a/pd/src/d_fftroutine.c b/pd/src/d_fftroutine.c index 4678d38a..0222a0c0 100644 --- a/pd/src/d_fftroutine.c +++ b/pd/src/d_fftroutine.c @@ -98,7 +98,7 @@ #define FALSE 0 #endif -#define SAMPLE float /* data type used in calculation */ +#define SAMPLE PD_FLOATTYPE /* data type used in calculation */ #define SHORT_SIZE sizeof(short) #define INT_SIZE sizeof(int) @@ -154,8 +154,8 @@ typedef struct Tfft_net { void cfft(int trnsfrm_dir, int npnt, int window, - float *source_buf, int source_form, int source_scale, - float *result_buf, int result_form, int result_scale, int debug); + SAMPLE *source_buf, int source_form, int source_scale, + SAMPLE *result_buf, int result_form, int result_scale, int debug); /*****************************************************************************/ @@ -172,10 +172,10 @@ int power_of_two(int n); void create_hanning(SAMPLE *window, int n, SAMPLE scale); void create_rectangular(SAMPLE *window, int n, SAMPLE scale); void short_to_float(short *short_buf, float *float_buf, int n); -void load_registers(FFT_NET *fft_net, float *buf, int buf_form, +void load_registers(FFT_NET *fft_net, SAMPLE *buf, int buf_form, int buf_scale, int trnsfrm_dir); void compute_fft(FFT_NET *fft_net); -void store_registers(FFT_NET *fft_net, float *buf, int buf_form, +void store_registers(FFT_NET *fft_net, SAMPLE *buf, int buf_form, int buf_scale, int debug); void build_fft_network(FFT_NET *fft_net, int n, int window_type); @@ -184,8 +184,8 @@ void build_fft_network(FFT_NET *fft_net, int n, int window_type); /*****************************************************************************/ void cfft(int trnsfrm_dir, int npnt, int window, - float *source_buf, int source_form, int source_scale, - float *result_buf, int result_form, int result_scale, int debug) + SAMPLE *source_buf, int source_form, int source_scale, + SAMPLE *result_buf, int result_form, int result_scale, int debug) /* modifies: result_buf effects: Computes npnt FFT specified by form, scale, and dir parameters. @@ -471,7 +471,7 @@ void build_fft_network(FFT_NET *fft_net, int n, int window_type) /* REGISTER LOAD AND STORE */ /*****************************************************************************/ -void load_registers(FFT_NET *fft_net, float *buf, int buf_form, +void load_registers(FFT_NET *fft_net, SAMPLE *buf, int buf_form, int buf_scale, int trnsfrm_dir) /* effects: Multiplies the input buffer with the appropriate window and @@ -605,7 +605,7 @@ void load_registers(FFT_NET *fft_net, float *buf, int buf_form, } -void store_registers(FFT_NET *fft_net, float *buf, int buf_form, +void store_registers(FFT_NET *fft_net, SAMPLE *buf, int buf_form, int buf_scale, int debug) /* modifies: buf @@ -989,10 +989,10 @@ void short_to_float(short *short_buf, float *float_buf, int n) /* here's the meat: */ -void pd_fft(float *buf, int npoints, int inverse) +void pd_fft(t_float *buf, int npoints, int inverse) { double renorm; - float *fp, *fp2; + SAMPLE *fp, *fp2; int i; renorm = (inverse ? npoints : 1.); cfft((inverse ? INVERSE : FORWARD), npoints, RECTANGULAR, diff --git a/pd/src/d_math.c b/pd/src/d_math.c index 213b866e..738f2d6c 100644 --- a/pd/src/d_math.c +++ b/pd/src/d_math.c @@ -97,7 +97,7 @@ static void init_rsqrt(void) /* these are used in externs like "bonk" */ -float q8_rsqrt(float f) +t_float q8_rsqrt(t_float f) { long l = *(long *)(&f); if (f < 0) return (0); @@ -105,7 +105,7 @@ float q8_rsqrt(float f) rsqrt_mantissatab[(l >> 13) & 0x3ff]); } -float q8_sqrt(float f) +t_float q8_sqrt(t_float f) { long l = *(long *)(&f); if (f < 0) return (0); @@ -116,8 +116,8 @@ float q8_sqrt(float f) /* the old names are OK unless we're in IRIX N32 */ #ifndef N32 -float qsqrt(float f) {return (q8_sqrt(f)); } -float qrsqrt(float f) {return (q8_rsqrt(f)); } +t_float qsqrt(t_float f) {return (q8_sqrt(f)); } +t_float qrsqrt(t_float f) {return (q8_rsqrt(f)); } #endif @@ -555,6 +555,189 @@ void powtodb_tilde_setup(void) class_addmethod(powtodb_tilde_class, (t_method)powtodb_tilde_dsp, gensym("dsp"), 0); } +/* ----------------------------- pow ----------------------------- */ +static t_class *pow_tilde_class; + +typedef struct _pow_tilde +{ + t_object x_obj; + t_float x_f; +} t_pow_tilde; + +static void *pow_tilde_new(t_symbol *s, int argc, t_atom *argv) +{ + t_pow_tilde *x = (t_pow_tilde *)pd_new(pow_tilde_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); +} + +t_int *pow_tilde_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) + { + float f = *in1++; + if (f > 0) + *out = pow(f, *in2); + else *out = 0; + out++; + in2++; + } + return (w+5); +} + +static void pow_tilde_dsp(t_pow_tilde *x, t_signal **sp) +{ + dsp_add(pow_tilde_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); +} + +static void pow_tilde_setup(void) +{ + pow_tilde_class = class_new(gensym("pow~"), (t_newmethod)pow_tilde_new, 0, + sizeof(t_pow_tilde), 0, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(pow_tilde_class, t_pow_tilde, x_f); + class_addmethod(pow_tilde_class, (t_method)pow_tilde_dsp, gensym("dsp"), 0); +} + +/* ----------------------------- exp ----------------------------- */ +static t_class *exp_tilde_class; + +typedef struct _exp_tilde +{ + t_object x_obj; + t_float x_f; +} t_exp_tilde; + +static void *exp_tilde_new(t_symbol *s, int argc, t_atom *argv) +{ + t_exp_tilde *x = (t_exp_tilde *)pd_new(exp_tilde_class); + outlet_new(&x->x_obj, &s_signal); + return (x); +} + +t_int *exp_tilde_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *out = (t_sample *)(w[2]); + int n = (int)(w[3]); + while (n--) + *out = exp(*in1); + return (w+4); +} + +static void exp_tilde_dsp(t_exp_tilde *x, t_signal **sp) +{ + dsp_add(exp_tilde_perform, 3, + sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +static void exp_tilde_setup(void) +{ + exp_tilde_class = class_new(gensym("exp~"), (t_newmethod)exp_tilde_new, 0, + sizeof(t_exp_tilde), 0, 0); + CLASS_MAINSIGNALIN(exp_tilde_class, t_exp_tilde, x_f); + class_addmethod(exp_tilde_class, (t_method)exp_tilde_dsp, gensym("dsp"), 0); +} + +/* ----------------------------- log ----------------------------- */ +static t_class *log_tilde_class; + +typedef struct _log_tilde +{ + t_object x_obj; + t_float x_f; +} t_log_tilde; + +static void *log_tilde_new(t_symbol *s, int argc, t_atom *argv) +{ + t_log_tilde *x = (t_log_tilde *)pd_new(log_tilde_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); +} + +t_int *log_tilde_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) + { + float f = *in1++, g = *in2++; + if (f <= 0) + *out = -1000; /* rather than blow up, output a number << 0 */ + else if (g <= 0) + *out = log(f); + else *out = log(f)/log(g); + out++; + } + return (w+5); +} + +static void log_tilde_dsp(t_log_tilde *x, t_signal **sp) +{ + dsp_add(log_tilde_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); +} + +static void log_tilde_setup(void) +{ + log_tilde_class = class_new(gensym("log~"), (t_newmethod)log_tilde_new, 0, + sizeof(t_log_tilde), 0, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(log_tilde_class, t_log_tilde, x_f); + class_addmethod(log_tilde_class, (t_method)log_tilde_dsp, gensym("dsp"), 0); +} + +/* ----------------------------- abs ----------------------------- */ +static t_class *abs_tilde_class; + +typedef struct _abs_tilde +{ + t_object x_obj; + t_float x_f; +} t_abs_tilde; + +static void *abs_tilde_new(t_symbol *s, int argc, t_atom *argv) +{ + t_abs_tilde *x = (t_abs_tilde *)pd_new(abs_tilde_class); + outlet_new(&x->x_obj, &s_signal); + return (x); +} + +t_int *abs_tilde_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *out = (t_sample *)(w[2]); + int n = (int)(w[3]); + while (n--) + { + float f = *in1++; + *out = (f >= 0 ? f : -f); + } + return (w+4); +} + +static void abs_tilde_dsp(t_abs_tilde *x, t_signal **sp) +{ + dsp_add(abs_tilde_perform, 3, + sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +static void abs_tilde_setup(void) +{ + abs_tilde_class = class_new(gensym("abs~"), (t_newmethod)abs_tilde_new, 0, + sizeof(t_abs_tilde), 0, 0); + CLASS_MAINSIGNALIN(abs_tilde_class, t_abs_tilde, x_f); + class_addmethod(abs_tilde_class, (t_method)abs_tilde_dsp, gensym("dsp"), 0); +} /* ------------------------ global setup routine ------------------------- */ @@ -571,6 +754,10 @@ void d_math_setup(void) rmstodb_tilde_setup(); dbtopow_tilde_setup(); powtodb_tilde_setup(); + pow_tilde_setup(); + exp_tilde_setup(); + log_tilde_setup(); + abs_tilde_setup(); class_sethelpsymbol(mtof_tilde_class, s); class_sethelpsymbol(ftom_tilde_class, s); diff --git a/pd/src/g_array.c b/pd/src/g_array.c index 0ebc99fb..a73c5ba5 100644 --- a/pd/src/g_array.c +++ b/pd/src/g_array.c @@ -1402,13 +1402,14 @@ static void garray_read(t_garray *x, t_symbol *filename) } for (i = 0; i < nelem; i++) { - if (!fscanf(fd, "%f", ((t_float *)(array->a_vec + - elemsize * i) + yonset))) + float f; + if (!fscanf(fd, "%f", &f)) { post("%s: read %d elements into table of size %d", filename->s_name, i, nelem); break; } + else *((t_float *)(array->a_vec + elemsize * i) + yonset) = f; } while (i < nelem) *((t_float *)(array->a_vec + diff --git a/pd/src/g_canvas.c b/pd/src/g_canvas.c index 36d3d3d6..c6481d28 100644 --- a/pd/src/g_canvas.c +++ b/pd/src/g_canvas.c @@ -47,7 +47,7 @@ static void canvas_start_dsp(void); static void canvas_stop_dsp(void); static void canvas_drawlines(t_canvas *x); static void canvas_setbounds(t_canvas *x, int x1, int y1, int x2, int y2); -static void canvas_reflecttitle(t_canvas *x); +void canvas_reflecttitle(t_canvas *x); static void canvas_addtolist(t_canvas *x); static void canvas_takeofflist(t_canvas *x); static void canvas_pop(t_canvas *x, t_floatarg fvis); @@ -682,144 +682,6 @@ void canvas_redraw(t_canvas *x) } } -/* ---- editors -- perhaps this and "vis" should go to g_editor.c ------- */ - -static t_editor *editor_new(t_glist *owner) -{ - char buf[40]; - t_editor *x = (t_editor *)getbytes(sizeof(*x)); - x->e_connectbuf = binbuf_new(); - x->e_deleted = binbuf_new(); - x->e_glist = owner; - sprintf(buf, ".x%lx", (t_int)owner); - x->e_guiconnect = guiconnect_new(&owner->gl_pd, gensym(buf)); - return (x); -} - -static void editor_free(t_editor *x, t_glist *y) -{ - glist_noselect(y); - guiconnect_notarget(x->e_guiconnect, 1000); - binbuf_free(x->e_connectbuf); - binbuf_free(x->e_deleted); - freebytes((void *)x, sizeof(*x)); -} - - /* recursively create or destroy all editors of a glist and its - sub-glists, as long as they aren't toplevels. */ -void canvas_create_editor(t_glist *x, int createit) -{ - t_gobj *y; - t_object *ob; - if (createit) - { - if (x->gl_editor) - bug("canvas_create_editor"); - else - { - x->gl_editor = editor_new(x); - for (y = x->gl_list; y; y = y->g_next) - if (ob = pd_checkobject(&y->g_pd)) - rtext_new(x, ob); - } - } - else - { - if (!x->gl_editor) - bug("canvas_create_editor"); - else - { - for (y = x->gl_list; y; y = y->g_next) - if (ob = pd_checkobject(&y->g_pd)) - rtext_free(glist_findrtext(x, ob)); - editor_free(x->gl_editor, x); - x->gl_editor = 0; - } - } - for (y = x->gl_list; y; y = y->g_next) - if (pd_class(&y->g_pd) == canvas_class && - ((t_canvas *)y)->gl_isgraph && !((t_canvas *)y)->gl_havewindow) - canvas_create_editor((t_canvas *)y, createit); -} - - /* we call this when we want the window to become visible, mapped, and - in front of all windows; or with "f" zero, when we want to get rid of - the window. */ -void canvas_vis(t_canvas *x, t_floatarg f) -{ - char buf[30]; - int flag = (f != 0); - if (flag) - { - /* post("havewindow %d, isgraph %d, isvisible %d editor %d", - x->gl_havewindow, x->gl_isgraph, glist_isvisible(x), - (x->gl_editor != 0)); */ - /* test if we're already visible and toplevel */ - if (x->gl_editor) - { /* just put us in front */ -#ifdef MSW - canvas_vis(x, 0); - canvas_vis(x, 1); -#else - sys_vgui("raise .x%lx\n", x); - sys_vgui("focus .x%lx.c\n", x); - sys_vgui("wm deiconify .x%lx\n", x); -#endif - } - else - { - canvas_create_editor(x, 1); - sys_vgui("pdtk_canvas_new .x%lx %d %d +%d+%d %d\n", x, - (int)(x->gl_screenx2 - x->gl_screenx1), - (int)(x->gl_screeny2 - x->gl_screeny1), - (int)(x->gl_screenx1), (int)(x->gl_screeny1), - x->gl_edit); - canvas_reflecttitle(x); - x->gl_havewindow = 1; - canvas_updatewindowlist(); - } - } - else /* make invisible */ - { - int i; - t_canvas *x2; - if (!x->gl_havewindow) - { - /* bug workaround -- a graph in a visible patch gets "invised" - when the patch is closed, and must lose the editor here. It's - probably not the natural place to do this. Other cases like - subpatches fall here too but don'd need the editor freed, so - we check if it exists. */ - if (x->gl_editor) - canvas_create_editor(x, 0); - return; - } - sys_vgui("pdtk_canvas_getscroll .x%lx.c\n", x); - glist_noselect(x); - if (glist_isvisible(x)) - canvas_map(x, 0); - canvas_create_editor(x, 0); - sys_vgui("destroy .x%lx\n", x); - for (i = 1, x2 = x; x2; x2 = x2->gl_next, i++) - ; - sys_vgui(".mbar.find delete %d\n", i); - /* if we're a graph on our parent, and if the parent exists - and is visible, show ourselves on parent. */ - if (glist_isgraph(x) && x->gl_owner) - { - t_glist *gl2 = x->gl_owner; - if (!x->gl_owner->gl_isdeleting) - canvas_create_editor(x, 1); - if (glist_isvisible(gl2)) - gobj_vis(&x->gl_gobj, gl2, 0); - x->gl_havewindow = 0; - if (glist_isvisible(gl2)) - gobj_vis(&x->gl_gobj, gl2, 1); - } - else x->gl_havewindow = 0; - canvas_updatewindowlist(); - } -} /* we call this on a non-toplevel glist to "open" it into its own window. */ @@ -835,7 +697,8 @@ void glist_menu_open(t_glist *x) /* erase ourself in parent window */ gobj_vis(&x->gl_gobj, gl2, 0); /* get rid of our editor (and subeditors) */ - canvas_create_editor(x, 0); + if (x->gl_editor) + canvas_create_editor(x, 0); x->gl_havewindow = 1; /* redraw ourself in parent window (blanked out this time) */ gobj_vis(&x->gl_gobj, gl2, 1); @@ -1475,6 +1338,22 @@ void canvas_savedeclarationsto(t_canvas *x, t_binbuf *b) } } +static void canvas_completepath(char *from, char *to) +{ + if (sys_isabsolutepath(from)) + { + to[0] = '\0'; + } + else + { // if not absolute path, append Pd lib dir + strncpy(to, sys_libdir->s_name, FILENAME_MAX-4); + to[FILENAME_MAX-3] = '\0'; + strcat(to, "/"); + } + strncat(to, from, FILENAME_MAX-strlen(to)); + to[FILENAME_MAX-1] = '\0'; +} + static void canvas_declare(t_canvas *x, t_symbol *s, int argc, t_atom *argv) { int i; @@ -1496,12 +1375,7 @@ static void canvas_declare(t_canvas *x, t_symbol *s, int argc, t_atom *argv) } else if ((argc > i+1) && !strcmp(flag, "-stdpath")) { - strncpy(strbuf, sys_libdir->s_name, MAXPDSTRING-3); - strbuf[MAXPDSTRING-4] = 0; - strcat(strbuf, "/"); - strncpy(strbuf, atom_getsymbolarg(i+1, argc, argv)->s_name, - MAXPDSTRING-strlen(strbuf)); - strbuf[MAXPDSTRING-1] = 0; + canvas_completepath(atom_getsymbolarg(i+1, argc, argv)->s_name, strbuf); e->ce_path = namelist_append(e->ce_path, strbuf, 0); i++; } @@ -1512,12 +1386,7 @@ static void canvas_declare(t_canvas *x, t_symbol *s, int argc, t_atom *argv) } else if ((argc > i+1) && !strcmp(flag, "-stdlib")) { - strncpy(strbuf, sys_libdir->s_name, MAXPDSTRING-3); - strbuf[MAXPDSTRING-4] = 0; - strcat(strbuf, "/"); - strncpy(strbuf, atom_getsymbolarg(i+1, argc, argv)->s_name, - MAXPDSTRING-strlen(strbuf)); - strbuf[MAXPDSTRING-1] = 0; + canvas_completepath(atom_getsymbolarg(i+1, argc, argv)->s_name, strbuf); sys_load_lib(0, strbuf); i++; } @@ -1564,9 +1433,16 @@ int canvas_open(t_canvas *x, const char *name, const char *ext, for (nl = y->gl_env->ce_path; nl; nl = nl->nl_next) { char realname[MAXPDSTRING]; - strncpy(realname, dir, MAXPDSTRING); - realname[MAXPDSTRING-3] = 0; - strcat(realname, "/"); + if (sys_isabsolutepath(nl->nl_string)) + { + realname[0] = '\0'; + } + else + { /* if not absolute path, append Pd lib dir */ + strncpy(realname, dir, MAXPDSTRING); + realname[MAXPDSTRING-3] = 0; + strcat(realname, "/"); + } strncat(realname, nl->nl_string, MAXPDSTRING-strlen(realname)); realname[MAXPDSTRING-1] = 0; if ((fd = sys_trytoopenone(realname, name, ext, diff --git a/pd/src/g_canvas.h b/pd/src/g_canvas.h index e2b6626a..c074ad5c 100644 --- a/pd/src/g_canvas.h +++ b/pd/src/g_canvas.h @@ -7,8 +7,8 @@ functions. "Glists" and "canvases" and "graphs" used to be different structures until being unified in version 0.35. A glist occupies its own window if the "gl_havewindow" flag is set. Its -appearance on its "parent" or "owner" (if it has one) is as a graph if -"gl_isgraph" is set, and otherwise as a text box. +appearance on its "parent", also called "owner", (if it has one) is as a graph +if "gl_isgraph" is set, and otherwise as a text box. A glist is "root" if it has no owner, i.e., a document window. In this case "gl_havewindow" is always set. @@ -353,6 +353,7 @@ EXTERN int gobj_click(t_gobj *x, struct _glist *glist, EXTERN void gobj_save(t_gobj *x, t_binbuf *b); EXTERN void gobj_properties(t_gobj *x, struct _glist *glist); EXTERN void gobj_save(t_gobj *x, t_binbuf *b); +EXTERN int gobj_shouldvis(t_gobj *x, struct _glist *glist); /* -------------------- functions on glists --------------------- */ EXTERN t_glist *glist_new( void); @@ -414,7 +415,6 @@ EXTERN int text_xcoord(t_text *x, t_glist *glist); EXTERN int text_ycoord(t_text *x, t_glist *glist); EXTERN int text_xpix(t_text *x, t_glist *glist); EXTERN int text_ypix(t_text *x, t_glist *glist); -EXTERN int text_shouldvis(t_text *x, t_glist *glist); /* -------------------- functions on rtexts ------------------------- */ #define RTEXT_DOWN 1 diff --git a/pd/src/g_editor.c b/pd/src/g_editor.c index a44d952a..ee2a76ac 100644 --- a/pd/src/g_editor.c +++ b/pd/src/g_editor.c @@ -61,28 +61,43 @@ void gobj_delete(t_gobj *x, t_glist *glist) (*x->g_pd->c_wb->w_deletefn)(x, glist); } +int gobj_shouldvis(t_gobj *x, struct _glist *glist) +{ + t_object *ob; + if (!glist->gl_havewindow && glist->gl_isgraph && glist->gl_goprect && + glist->gl_owner && (pd_class(&glist->gl_pd) != garray_class)) + { + /* if we're graphing-on-parent and the object falls outside the + graph rectangle, don't draw it. */ + int x1, y1, x2, y2, gx1, gy1, gx2, gy2, m; + gobj_getrect(&glist->gl_gobj, glist->gl_owner, &x1, &y1, &x2, &y2); + if (x1 > x2) + m = x1, x1 = x2, x2 = m; + if (y1 > y2) + m = y1, y1 = y2, y2 = m; + gobj_getrect(x, glist, &gx1, &gy1, &gx2, &gy2); + if (gx1 < x1 || gx1 > x2 || gx2 < x1 || gx2 > x2 || + gy1 < y1 || gy1 > y2 || gy2 < y1 || gy2 > y2) + return (0); + } + if (ob = pd_checkobject(&x->g_pd)) + { + /* return true if the text box should be drawn. We don't show text + boxes inside graphs---except comments, if we're doing the new + (goprect) style. */ + return (glist->gl_havewindow || + (ob->te_pd != canvas_class && + ob->te_pd->c_wb != &text_widgetbehavior) || + (ob->te_pd == canvas_class && (((t_glist *)ob)->gl_isgraph)) || + (glist->gl_goprect && (ob->te_type == T_TEXT))); + } + else return (1); +} + void gobj_vis(t_gobj *x, struct _glist *glist, int flag) { - if (x->g_pd->c_wb && x->g_pd->c_wb->w_visfn) - { - if (!glist->gl_havewindow && glist->gl_isgraph && glist->gl_goprect && - glist->gl_owner && (pd_class(&glist->gl_pd) != garray_class)) - { - /* if we're graphing-on-parent and the object falls outside the - graph rectangle, don't draw it. */ - int x1, y1, x2, y2, gx1, gy1, gx2, gy2, m; - gobj_getrect(&glist->gl_gobj, glist->gl_owner, &x1, &y1, &x2, &y2); - if (x1 > x2) - m = x1, x1 = x2, x2 = m; - if (y1 > y2) - m = y1, y1 = y2, y2 = m; - gobj_getrect(x, glist, &gx1, &gy1, &gx2, &gy2); - if (gx1 < x1 || gx1 > x2 || gx2 < x1 || gx2 > x2 || - gy1 < y1 || gy1 > y2 || gy2 < y1 || gy2 > y2) - return; - } + if (x->g_pd->c_wb && x->g_pd->c_wb->w_visfn && gobj_shouldvis(x, glist)) (*x->g_pd->c_wb->w_visfn)(x, glist, flag); - } } int gobj_click(t_gobj *x, struct _glist *glist, @@ -765,9 +780,8 @@ int canvas_hitbox(t_canvas *x, t_gobj *y, int xpos, int ypos, { int x1, y1, x2, y2; t_text *ob; - if ((ob = pd_checkobject(&y->g_pd)) && - !text_shouldvis(ob, x)) - return (0); + if (!gobj_shouldvis(y, x)) + return (0); gobj_getrect(y, x, &x1, &y1, &x2, &y2); if (xpos >= x1 && xpos <= x2 && ypos >= y1 && ypos <= y2) { @@ -817,12 +831,157 @@ static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y) x, xpos, ypos, canprop, canopen); } +/* ---- editors -- perhaps this and "vis" should go to g_editor.c ------- */ + +static t_editor *editor_new(t_glist *owner) +{ + char buf[40]; + t_editor *x = (t_editor *)getbytes(sizeof(*x)); + x->e_connectbuf = binbuf_new(); + x->e_deleted = binbuf_new(); + x->e_glist = owner; + sprintf(buf, ".x%lx", (t_int)owner); + x->e_guiconnect = guiconnect_new(&owner->gl_pd, gensym(buf)); + return (x); +} + +static void editor_free(t_editor *x, t_glist *y) +{ + glist_noselect(y); + guiconnect_notarget(x->e_guiconnect, 1000); + binbuf_free(x->e_connectbuf); + binbuf_free(x->e_deleted); + freebytes((void *)x, sizeof(*x)); +} + + /* recursively create or destroy all editors of a glist and its + sub-glists, as long as they aren't toplevels. */ +void canvas_create_editor(t_glist *x, int createit) +{ + t_gobj *y; + t_object *ob; + if (createit) + { + if (x->gl_editor) + bug("canvas_create_editor"); + else + { + x->gl_editor = editor_new(x); + for (y = x->gl_list; y; y = y->g_next) + if (ob = pd_checkobject(&y->g_pd)) + rtext_new(x, ob); + } + } + else + { + if (!x->gl_editor) + bug("canvas_create_editor"); + else + { + for (y = x->gl_list; y; y = y->g_next) + if (ob = pd_checkobject(&y->g_pd)) + rtext_free(glist_findrtext(x, ob)); + editor_free(x->gl_editor, x); + x->gl_editor = 0; + } + } + for (y = x->gl_list; y; y = y->g_next) + if (pd_class(&y->g_pd) == canvas_class && + ((t_canvas *)y)->gl_isgraph && !((t_canvas *)y)->gl_havewindow) + canvas_create_editor((t_canvas *)y, createit); +} + +void canvas_reflecttitle(t_canvas *x); +void canvas_map(t_canvas *x, t_floatarg f); + + /* we call this when we want the window to become visible, mapped, and + in front of all windows; or with "f" zero, when we want to get rid of + the window. */ +void canvas_vis(t_canvas *x, t_floatarg f) +{ + char buf[30]; + int flag = (f != 0); + if (flag) + { + /* post("havewindow %d, isgraph %d, isvisible %d editor %d", + x->gl_havewindow, x->gl_isgraph, glist_isvisible(x), + (x->gl_editor != 0)); */ + /* test if we're already visible and toplevel */ + if (x->gl_editor) + { /* just put us in front */ +#ifdef MSW + canvas_vis(x, 0); + canvas_vis(x, 1); +#else + sys_vgui("raise .x%lx\n", x); + sys_vgui("focus .x%lx.c\n", x); + sys_vgui("wm deiconify .x%lx\n", x); +#endif + } + else + { + canvas_create_editor(x, 1); + sys_vgui("pdtk_canvas_new .x%lx %d %d +%d+%d %d\n", x, + (int)(x->gl_screenx2 - x->gl_screenx1), + (int)(x->gl_screeny2 - x->gl_screeny1), + (int)(x->gl_screenx1), (int)(x->gl_screeny1), + x->gl_edit); + canvas_reflecttitle(x); + x->gl_havewindow = 1; + canvas_updatewindowlist(); + } + } + else /* make invisible */ + { + int i; + t_canvas *x2; + if (!x->gl_havewindow) + { + /* bug workaround -- a graph in a visible patch gets "invised" + when the patch is closed, and must lose the editor here. It's + probably not the natural place to do this. Other cases like + subpatches fall here too but don'd need the editor freed, so + we check if it exists. */ + if (x->gl_editor) + canvas_create_editor(x, 0); + return; + } + sys_vgui("pdtk_canvas_getscroll .x%lx.c\n", x); + glist_noselect(x); + if (glist_isvisible(x)) + canvas_map(x, 0); + canvas_create_editor(x, 0); + sys_vgui("destroy .x%lx\n", x); + for (i = 1, x2 = x; x2; x2 = x2->gl_next, i++) + ; + sys_vgui(".mbar.find delete %d\n", i); + /* if we're a graph on our parent, and if the parent exists + and is visible, show ourselves on parent. */ + if (glist_isgraph(x) && x->gl_owner) + { + t_glist *gl2 = x->gl_owner; + if (!x->gl_owner->gl_isdeleting) + canvas_create_editor(x, 1); + if (glist_isvisible(gl2)) + gobj_vis(&x->gl_gobj, gl2, 0); + x->gl_havewindow = 0; + if (glist_isvisible(gl2)) + gobj_vis(&x->gl_gobj, gl2, 1); + } + else x->gl_havewindow = 0; + canvas_updatewindowlist(); + } +} + /* set a canvas up as a graph-on-parent. Set reasonable defaults for any missing paramters and redraw things if necessary. */ void canvas_setgraph(t_glist *x, int flag, int nogoprect) { if (!flag && glist_isgraph(x)) { + int hadeditor = (x->gl_editor != 0); + if (hadeditor) + canvas_create_editor(x, 0); if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner)) gobj_vis(&x->gl_gobj, x->gl_owner, 0); x->gl_isgraph = 0; @@ -831,6 +990,8 @@ void canvas_setgraph(t_glist *x, int flag, int nogoprect) gobj_vis(&x->gl_gobj, x->gl_owner, 1); canvas_fixlinesfor(x->gl_owner, &x->gl_obj); } + if (hadeditor) + canvas_create_editor(x, 1); } else if (flag) { diff --git a/pd/src/g_graph.c b/pd/src/g_graph.c index f4196def..cfda6c2c 100644 --- a/pd/src/g_graph.c +++ b/pd/src/g_graph.c @@ -176,8 +176,9 @@ void glist_grab(t_glist *x, t_gobj *y, t_glistmotionfn motionfn, t_canvas *glist_getcanvas(t_glist *x) { - while (x->gl_owner && !x->gl_havewindow && x->gl_isgraph) - x = x->gl_owner; + while (x->gl_owner && !x->gl_havewindow && x->gl_isgraph && + gobj_shouldvis(&x->gl_gobj, x->gl_owner)) + x = x->gl_owner; return((t_canvas *)x); } @@ -671,7 +672,6 @@ void glist_redraw(t_glist *x) /* --------------------------- widget behavior ------------------- */ -extern t_widgetbehavior text_widgetbehavior; int garray_getname(t_garray *x, t_symbol **namep); @@ -904,7 +904,7 @@ static void graph_getrect(t_gobj *z, t_glist *glist, hadwindow = x->gl_havewindow; x->gl_havewindow = 0; for (g = x->gl_list; g; g = g->g_next) - if ((!(ob = pd_checkobject(&g->g_pd))) || text_shouldvis(ob, x)) + if (gobj_shouldvis(g, x)) { /* don't do this for arrays, just let them hang outside the box. */ @@ -1107,8 +1107,6 @@ void g_graph_setup(void) A_SYMBOL, A_FLOAT, A_SYMBOL, A_DEFFLOAT, A_NULL); class_addmethod(canvas_class, (t_method)canvas_menuarray, gensym("menuarray"), A_NULL); - class_addmethod(canvas_class, (t_method)glist_arraydialog, - gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); class_addmethod(canvas_class, (t_method)glist_sort, gensym("sort"), A_NULL); } diff --git a/pd/src/g_template.c b/pd/src/g_template.c index 4d66bbed..aff6f7cb 100644 --- a/pd/src/g_template.c +++ b/pd/src/g_template.c @@ -721,11 +721,11 @@ struct _fielddesc t_symbol *fd_symbol; /* the field is a constant symbol */ t_symbol *fd_varsym; /* the field is variable and this is the name */ } fd_un; - t_float fd_v1; /* min and max values */ - t_float fd_v2; - t_float fd_screen1; /* min and max screen values */ - t_float fd_screen2; - t_float fd_quantum; /* quantization in value */ + float fd_v1; /* min and max values */ + float fd_v2; + float fd_screen1; /* min and max screen values */ + float fd_screen2; + float fd_quantum; /* quantization in value */ }; static void fielddesc_setfloat_const(t_fielddesc *fd, t_float f) @@ -2225,7 +2225,7 @@ static void drawnumber_key(void *z, t_floatarg fkey) else { /* key entry for a numeric field. This is just a stopgap. */ - t_float newf; + float newf; if (drawnumber_motion_firstkey) sbuf[0] = 0; else sprintf(sbuf, "%g", template_getfloat(drawnumber_motion_template, diff --git a/pd/src/g_text.c b/pd/src/g_text.c index bbdb7729..1d4559ec 100644 --- a/pd/src/g_text.c +++ b/pd/src/g_text.c @@ -989,7 +989,7 @@ static void text_select(t_gobj *z, t_glist *glist, int state) t_text *x = (t_text *)z; t_rtext *y = glist_findrtext(glist, x); rtext_select(y, state); - if (glist_isvisible(glist) && text_shouldvis(x, glist)) + if (glist_isvisible(glist) && gobj_shouldvis(&x->te_g, glist)) sys_vgui(".x%lx.c itemconfigure %sR -fill %s\n", glist, rtext_gettag(y), (state? "blue" : "black")); } @@ -1007,22 +1007,12 @@ static void text_delete(t_gobj *z, t_glist *glist) canvas_deletelinesfor(glist, x); } - /* return true if the text box should be drawn. We don't show text boxes - inside graphs---except comments, if we're doing the new (goprect) style. */ -int text_shouldvis(t_text *x, t_glist *glist) -{ - return (glist->gl_havewindow || - (x->te_pd != canvas_class && x->te_pd->c_wb != &text_widgetbehavior) || - (x->te_pd == canvas_class && (((t_glist *)x)->gl_isgraph)) || - (glist->gl_goprect && (x->te_type == T_TEXT))); -} - static void text_vis(t_gobj *z, t_glist *glist, int vis) { t_text *x = (t_text *)z; if (vis) { - if (text_shouldvis(x, glist)) + if (gobj_shouldvis(&x->te_g, glist)) { t_rtext *y = glist_findrtext(glist, x); if (x->te_type == T_ATOM) @@ -1035,7 +1025,7 @@ static void text_vis(t_gobj *z, t_glist *glist, int vis) else { t_rtext *y = glist_findrtext(glist, x); - if (text_shouldvis(x, glist)) + if (gobj_shouldvis(&x->te_g, glist)) { text_eraseborder(x, glist, rtext_gettag(y)); rtext_erase(y); diff --git a/pd/src/m_class.c b/pd/src/m_class.c index ab0b86be..aa44022d 100644 --- a/pd/src/m_class.c +++ b/pd/src/m_class.c @@ -18,6 +18,10 @@ #include #include +#ifdef _MSC_VER /* This is only for Microsoft's compiler, not cygwin, e.g. */ +#define snprintf sprintf_s +#endif + static t_symbol *class_loadsym; /* name under which an extern is invoked */ static void pd_defaultfloat(t_pd *x, t_float f); static void pd_defaultlist(t_pd *x, t_symbol *s, int argc, t_atom *argv); @@ -149,7 +153,6 @@ static void pd_defaultlist(t_pd *x, t_symbol *s, int argc, t_atom *argv) argument form, one for the multiple one; see select_setup() to find out how this is handled. */ -extern t_widgetbehavior text_widgetbehavior; extern void text_save(t_gobj *z, t_binbuf *b); t_class *class_new(t_symbol *s, t_newmethod newmethod, t_method freemethod, @@ -168,7 +171,7 @@ t_class *class_new(t_symbol *s, t_newmethod newmethod, t_method freemethod, { if (count == MAXPDARG) { - error("class %s: sorry: only %d creation args allowed", + error("class %s: sorry: only %d args typechecked; use A_GIMME", s->s_name, MAXPDARG); break; } @@ -300,6 +303,19 @@ void class_addmethod(t_class *c, t_method fn, t_symbol *sel, } else { + int i; + for (i = 0; i < c->c_nmethod; i++) + if (c->c_methods[i].me_name == sel) + { + char nbuf[80]; + snprintf(nbuf, 80, "%s_aliased", sel->s_name); + c->c_methods[i].me_name = gensym(nbuf); + if (c == pd_objectmaker) + post("warning: class '%s' overwritten; old one renamed '%s'", + sel->s_name, nbuf); + else post("warning: old method '%s' for class '%s' renamed '%s'", + sel->s_name, c->c_name->s_name, nbuf); + } c->c_methods = t_resizebytes(c->c_methods, c->c_nmethod * sizeof(*c->c_methods), (c->c_nmethod + 1) * sizeof(*c->c_methods)); diff --git a/pd/src/m_pd.h b/pd/src/m_pd.h index 580bac0b..f48a13b9 100644 --- a/pd/src/m_pd.h +++ b/pd/src/m_pd.h @@ -9,9 +9,9 @@ extern "C" { #endif #define PD_MAJOR_VERSION 0 -#define PD_MINOR_VERSION 41 -#define PD_BUGFIX_VERSION 4 -#define PD_TEST_VERSION "" +#define PD_MINOR_VERSION 42 +#define PD_BUGFIX_VERSION 0 +#define PD_TEST_VERSION "test1" /* old name for "MSW" flag -- we have to take it for the sake of many old "nmakefiles" for externs, which will define NT and not MSW */ @@ -55,11 +55,15 @@ extern "C" { #define MAXPDARG 5 /* max number of args we can typecheck today */ /* signed and unsigned integer types the size of a pointer: */ -/* GG: long is the size of a pointer */ -typedef long t_int; - -typedef float t_float; /* a floating-point number at most the same size */ -typedef float t_floatarg; /* floating-point type for function calls */ +#if !defined(PD_LONGINTTYPE) +#define PD_LONGINTTYPE long +#endif +#if !defined(PD_FLOATTYPE) +#define PD_FLOATTYPE float +#endif +typedef PD_LONGINTTYPE t_int; /* pointer-size integer */ +typedef PD_FLOATTYPE t_float; /* a float type at most the same size */ +typedef PD_FLOATTYPE t_floatarg; /* float type for function calls */ typedef struct _symbol { @@ -443,7 +447,7 @@ EXTERN t_propertiesfn class_getpropertiesfn(t_class *c); EXTERN void post(const char *fmt, ...); EXTERN void startpost(const char *fmt, ...); EXTERN void poststring(const char *s); -EXTERN void postfloat(float f); +EXTERN void postfloat(t_floatarg f); EXTERN void postatom(int argc, t_atom *argv); EXTERN void endpost(void); EXTERN void error(const char *fmt, ...); @@ -457,6 +461,7 @@ EXTERN void sys_ouch(void); /* ------------ system interface routines ------------------- */ EXTERN int sys_isreadablefile(const char *name); +EXTERN int sys_isabsolutepath(const char *dir); EXTERN void sys_bashfilename(const char *from, char *to); EXTERN void sys_unbashfilename(const char *from, char *to); EXTERN int open_via_path(const char *name, const char *ext, const char *dir, @@ -474,7 +479,7 @@ EXTERN int sys_trylock(void); /* --------------- signals ----------------------------------- */ -typedef float t_sample; +typedef PD_FLOATTYPE t_sample; #define MAXLOGSIG 32 #define MAXSIGSIZE (1 << MAXLOGSIG) diff --git a/pd/src/notes.txt b/pd/src/notes.txt index ea8ed631..8f94137f 100644 --- a/pd/src/notes.txt +++ b/pd/src/notes.txt @@ -43,6 +43,7 @@ scofo reports error on reading score1.txt loading e-mailed patches without removing headers crashes pd check if _vsnprintf with zero argument in windows works any better... detect adc~ and dac~ reblocking +wierd bug: help doesn't work if pd is started in 5.reference directory more demonstration patches: vibrato using variable delay @@ -50,6 +51,8 @@ real-time spectrum grapher document ||, |, etc, better features: +pasting should look at current mouse location +optionally suppress leading "." directories and files on "open" change config.h to #ifdef _MSC_VER (include MSW fake) else include a real one stick snprintf alias in the MSW fake. flag to prevent unlocking patches @@ -80,7 +83,6 @@ tables: flag to hide array names think of a way to embed abstractions in a patch make watchdog work for MACOSX -pasting should look at current mouse location delete-in-rectangle message to Pds put serial object in main dist (see rat@telecoma, Apr. 25; winfried May 22) open/save panel to take messages to init directory, and to set extent list diff --git a/pd/src/s_main.c b/pd/src/s_main.c index 877ed41f..dcc3bed9 100644 --- a/pd/src/s_main.c +++ b/pd/src/s_main.c @@ -282,7 +282,8 @@ int sys_main(int argc, char **argv) if (!noprefs) sys_loadpreferences(); /* load default settings */ #ifndef MSW - sys_rcfile(); /* parse the startup file */ + if (!noprefs) + sys_rcfile(); /* parse the startup file */ #endif if (sys_argparse(argc-1, argv+1)) /* parse cmd line */ return (1); diff --git a/pd/src/s_path.c b/pd/src/s_path.c index f59f09c7..ef441184 100644 --- a/pd/src/s_path.c +++ b/pd/src/s_path.c @@ -69,6 +69,24 @@ void sys_unbashfilename(const char *from, char *to) *to = 0; } +/* test if path is absolute or relative, based on leading /, env vars, ~, etc */ +int sys_isabsolutepath(const char *dir) +{ + if (dir[0] == '/' || dir[0] == '~' +#ifdef MSW + || dir[0] == '%' || (dir[1] == ':' && dir[2] == '/') +#endif + ) + { + return 1; + } + else + { + return 0; + } +} + + /******************* Utility functions used below ******************/ /*! @@ -250,11 +268,7 @@ int sys_trytoopenone(const char *dir, const char *name, const char* ext, int sys_open_absolute(const char *name, const char* ext, char *dirresult, char **nameresult, unsigned int size, int bin, int *fdp) { - if (name[0] == '/' -#ifdef MSW - || (name[1] == ':' && name[2] == '/') -#endif - ) + if (sys_isabsolutepath(name)) { char dirbuf[MAXPDSTRING]; int dirlen = (strrchr(name, '/') - name); diff --git a/pd/src/s_stuff.h b/pd/src/s_stuff.h index a1f9026c..e53d3edc 100644 --- a/pd/src/s_stuff.h +++ b/pd/src/s_stuff.h @@ -325,3 +325,4 @@ EXTERN void inmidi_polyaftertouch(int portno, int pitch, int value); /* } jsarlo */ +extern t_widgetbehavior text_widgetbehavior; diff --git a/pd/src/x_arithmetic.c b/pd/src/x_arithmetic.c index f64c8cbd..0dd19937 100644 --- a/pd/src/x_arithmetic.c +++ b/pd/src/x_arithmetic.c @@ -623,7 +623,6 @@ static void log_float(t_object *x, t_float f) outlet_float(x->ob_outlet, r); } - static t_class *exp_class; /* ----------- exp --------------- */ static void *exp_new(void) @@ -659,6 +658,20 @@ static void abs_float(t_object *x, t_float f) outlet_float(x->ob_outlet, fabsf(f)); } +static t_class *wrap_class; /* ----------- wrap --------------- */ + +static void *wrap_new(void) +{ + t_object *x = (t_object *)pd_new(wrap_class); + outlet_new(x, &s_float); + return (x); +} + +static void wrap_float(t_object *x, t_float f) +{ + outlet_float(x->ob_outlet, f - floor(f)); +} + /* ------------------------ misc ------------------------ */ static t_class *clip_class; @@ -898,6 +911,11 @@ void x_arithmetic_setup(void) class_addfloat(abs_class, (t_method)abs_float); class_sethelpsymbol(abs_class, math_sym); + wrap_class = class_new(gensym("wrap"), wrap_new, 0, + sizeof(t_object), 0, 0); + class_addfloat(wrap_class, (t_method)wrap_float); + class_sethelpsymbol(wrap_class, math_sym); + /* ------------------------ misc ------------------------ */ clip_setup(); diff --git a/pd/src/x_time.c b/pd/src/x_time.c index 60dcf9a5..5dc9d37f 100644 --- a/pd/src/x_time.c +++ b/pd/src/x_time.c @@ -334,7 +334,7 @@ static void *pipe_new(t_symbol *s, int argc, t_atom *argv) { char stupid[80]; atom_string(&argv[argc-1], stupid, 79); - post("pipe: %s: bad time delay value", stupid); + pd_error(x, "pipe: %s: bad time delay value", stupid); deltime = 0; } else deltime = argv[argc-1].a_w.w_float; @@ -385,7 +385,7 @@ static void *pipe_new(t_symbol *s, int argc, t_atom *argv) } else { - if (c != 'f') error("pack: %s: bad type", + if (c != 'f') pd_error(x, "pipe: %s: bad type", ap->a_w.w_symbol->s_name); SETFLOAT(&vp->p_atom, 0); vp->p_outlet = outlet_new(&x->x_obj, &s_float); @@ -437,7 +437,7 @@ static void hang_tick(t_hang *h) case A_POINTER: if (gpointer_check(w->w_gpointer, 1)) outlet_pointer(p->p_outlet, w->w_gpointer); - else post("pipe: stale pointer"); + else pd_error(x, "pipe: stale pointer"); break; } } @@ -454,7 +454,13 @@ static void pipe_list(t_pipe *x, t_symbol *s, int ac, t_atom *av) t_atom *ap; t_word *w; h->h_gp = (t_gpointer *)getbytes(x->x_nptr * sizeof(t_gpointer)); - if (ac > n) ac = n; + if (ac > n) + { + if (av[n].a_type == A_FLOAT) + x->x_deltime = av[n].a_w.w_float; + else pd_error(x, "pipe: symbol or pointer in time inlet"); + ac = n; + } for (i = 0, gp = x->x_gp, p = x->x_vec, ap = av; i < ac; i++, p++, ap++) { @@ -465,7 +471,7 @@ static void pipe_list(t_pipe *x, t_symbol *s, int ac, t_atom *av) case A_POINTER: gpointer_unset(gp); if (ap->a_type != A_POINTER) - post("pipe: bad pointer"); + pd_error(x, "pipe: bad pointer"); else { *gp = *(ap->a_w.w_gpointer); -- cgit v1.2.1