-
Notifications
You must be signed in to change notification settings - Fork 11
/
pdlua.c
3173 lines (2922 loc) · 105 KB
/
pdlua.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* This is a version hacked by Martin Peach 20110120 [email protected] */
/* Reformmatted the code and added some debug messages. Changed the name of the class to pdlua */
/** @file lua.c
* @brief pdlua -- a Lua embedding for Pd.
* @author Claude Heiland-Allen <[email protected]>
* @date 2008
* @version 0.6~svn
*
* Copyright (C) 2007,2008 Claude Heiland-Allen <[email protected]>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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.
*
*/
/* various C stuff, mainly for reading files */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/types.h> // for open
#include <sys/stat.h> // for open
#ifdef _MSC_VER
#include <io.h>
#include <fcntl.h> // for open
#ifndef PATH_MAX
#define PATH_MAX 1024 /* same with Mac OS X's syslimits.h */
#endif
#define read _read
#define close _close
#define ssize_t int
#define snprintf _snprintf
#else
#include <sys/fcntl.h> // for open
#include <unistd.h>
#endif
/* we use Lua */
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include "pdlua.h"
#include "s_stuff.h" // for sys_register_loader()
#include "m_imp.h" // for struct _class
#include "g_canvas.h"
/* BAD: support for Pd < 0.41 */
#include "pdlua_gfx.h"
typedef void (*t_signal_setmultiout)(t_signal **, int);
static t_signal_setmultiout g_signal_setmultiout;
// This used to be in s_stuff.h, but not anymore since 0.55.1test1.
int sys_trytoopenone(const char *dir, const char *name, const char* ext,
char *dirresult, char **nameresult, unsigned int size, int bin);
// Check for absolute filenames in the second argument. Otherwise,
// sys_trytoopenone will happily prepend the given path anyway.
#define trytoopenone(dir, name, ...) sys_trytoopenone(sys_isabsolutepath(name) ? "" : dir, name, __VA_ARGS__)
#ifdef PDINSTANCE
typedef struct _lua_Instance {
void* pd_instance;
lua_State* state;
struct _lua_Instance* next;
} lua_Instance;
lua_Instance* lua_threads = NULL;
lua_State* __L()
{
lua_Instance* iter = lua_threads;
while(iter)
{
if(iter->pd_instance == pd_this)
{
return iter->state;
}
iter = iter->next;
}
return NULL; // should never happen
}
void initialise_lua_state()
{
if(!lua_threads)
{
lua_threads = t_getbytes(sizeof(lua_Instance));
lua_threads->pd_instance = pd_this;
lua_threads->state = luaL_newstate();
lua_threads->next = NULL;
return;
}
lua_Instance* iter = lua_threads;
while(iter->next)
{
iter = iter->next;
}
iter->next = t_getbytes(sizeof(lua_Instance));
iter->next->pd_instance = pd_this;
iter->next->state = lua_newthread(lua_threads->state);
iter->next->next = NULL;
}
#else
static lua_State* __lua_state = NULL;
lua_State* __L()
{
return __lua_state;
}
void initialise_lua_state()
{
if (!__lua_state) {
__lua_state = luaL_newstate();
}
}
#endif
#if PD_MAJOR_VERSION == 0
# if PD_MINOR_VERSION >= 41
# define PDLUA_PD41
/* use new garray support that is 64-bit safe */
# define PDLUA_ARRAYGRAB garray_getfloatwords
# define PDLUA_ARRAYTYPE t_word
# define PDLUA_ARRAYELEM(arr,idx) ((arr)[(idx)].w_float)
# elif PD_MINOR_VERSION >= 40
# define PDLUA_PD40
/* use old garray support, not 64-bit safe */
# define PDLUA_ARRAYGRAB garray_getfloatarray
# define PDLUA_ARRAYTYPE t_float
# define PDLUA_ARRAYELEM(arr,idx) ((arr)[(idx)])
# elif PD_MINOR_VERSION >= 39
# define PDLUA_PD39
/* use old garray support, not 64-bit safe */
# define PDLUA_ARRAYGRAB garray_getfloatarray
# define PDLUA_ARRAYTYPE t_float
# define PDLUA_ARRAYELEM(arr,idx) ((arr)[(idx)])
# else
# error "Pd version is too old, please upgrade"
# endif
#else
# error "Pd version is too new, please file a bug report"
#endif
#if PD_MINOR_VERSION >= 54
# ifndef PD_MULTICHANNEL
# define PD_MULTICHANNEL 1
# endif
#else
# pragma message("building without multi-channel support; requires Pd 0.54+")
# undef PD_MULTICHANNEL
# define PD_MULTICHANNEL 0
# define CLASS_MULTICHANNEL 0
#endif
#ifdef UNUSED
#elif defined(__GNUC__)
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
#elif defined(__LCLINT__)
# define UNUSED(x) /*@unused@*/ x
#else
# define UNUSED(x) x
#endif
/* BAD: end of bad section */
/* If defined, PDLUA_DEBUG lets pdlua post a lot of text */
//#define PDLUA_DEBUG post
#ifndef PDLUA_DEBUG
//static void PDLUA_DEBUG(const char *fmt, ...) {;}
# define PDLUA_DEBUG(x,y)
# define PDLUA_DEBUG2(x,y0,y1)
# define PDLUA_DEBUG3(x,y0,y1,y2)
#else
# define PDLUA_DEBUG2 PDLUA_DEBUG
# define PDLUA_DEBUG3 PDLUA_DEBUG
#endif
// In plugdata we're linked statically and thus c_externdir is empty.
// So we pass a data directory to the setup function instead and store it here.
// ag: Renamed to pdlua_datadir since we also need this in vanilla when
// setting up Lua's package.path.
char pdlua_datadir[MAXPDSTRING];
#if PLUGDATA
// Hook to inform plugdata which class names are lua objects
void(*plugdata_register_class)(const char*);
#endif
static char pdlua_cwd[MAXPDSTRING];
/** State for the Lua file reader. */
typedef struct pdlua_readerdata
{
int fd; /**< File descriptor to read from. */
char buffer[MAXPDSTRING]; /**< Buffer to read into. */
} t_pdlua_readerdata;
/** Proxy inlet object data. */
typedef struct pdlua_proxyinlet
{
t_pd pd; /**< Minimal Pd object. */
struct pdlua *owner; /**< The owning object to forward inlet messages to. */
unsigned int id; /**< The number of this inlet. */
} t_pdlua_proxyinlet;
/** Proxy receive object data. */
typedef struct pdlua_proxyreceive
{
t_pd pd; /**< Minimal Pd object. */
struct pdlua *owner; /**< The owning object to forward received messages to. */
t_symbol *name; /**< The receive-symbol to bind to. */
} t_pdlua_proxyreceive;
/** Proxy clock object data. */
typedef struct pdlua_proxyclock
{
t_pd pd; /**< Minimal Pd object. */
struct pdlua *owner; /**< Object to forward messages to. */
t_clock *clock; /** Pd clock to use. */
} t_pdlua_proxyclock;
/* prototypes*/
static const char *pdlua_reader (lua_State *L, void *rr, size_t *size);
/** Proxy inlet 'anything' method. */
static void pdlua_proxyinlet_anything (t_pdlua_proxyinlet *p, t_symbol *s, int argc, t_atom *argv);
/** Proxy inlet initialization. */
static void pdlua_proxyinlet_init (t_pdlua_proxyinlet *p, struct pdlua *owner, unsigned int id);
/** Register the proxy inlet class with Pd. */
static void pdlua_proxyinlet_setup (void);
/** Proxy receive 'anything' method. */
static void pdlua_proxyreceive_anything (t_pdlua_proxyreceive *r, t_symbol *s, int argc, t_atom *argv);
/** Proxy receive allocation and initialization. */
static t_pdlua_proxyreceive *pdlua_proxyreceive_new (struct pdlua *owner, t_symbol *name);
/** Proxy receive cleanup and deallocation. */
static void pdlua_proxyreceive_free (t_pdlua_proxyreceive *r /**< The proxy receive to free. */);
/** Register the proxy receive class with Pd. */
static void pdlua_proxyreceive_setup (void);
/** Proxy clock 'bang' method. */
static void pdlua_proxyclock_bang (t_pdlua_proxyclock *c);
/** Proxy clock allocation and initialization. */
static t_pdlua_proxyclock *pdlua_proxyclock_new (struct pdlua *owner);
/** Register the proxy clock class with Pd. */
static void pdlua_proxyclock_setup (void);
/** Dump an array of atoms into a Lua table. */
static void pdlua_pushatomtable (int argc, t_atom *argv);
/** Pd object constructor. */
static t_pdlua *pdlua_new (t_symbol *s, int argc, t_atom *argv);
/** Pd object destructor. */
static void pdlua_free (t_pdlua *o );
//static void pdlua_stack_dump (lua_State *L);
/** a handler for the open item in the right-click menu (mrpeach 20111025) */
/** Here we find the lua code for the object and open it in an editor */
static void pdlua_menu_open (t_pdlua *o);
/** Lua class registration. This is equivalent to the "setup" method for an ordinary Pd class */
static int pdlua_class_new (lua_State *L);
/** Lua object creation. */
static int pdlua_object_new (lua_State *L);
/** Lua object inlet creation. */
static int pdlua_object_createinlets (lua_State *L);
/** Lua object outlet creation. */
static int pdlua_object_createoutlets (lua_State *L);
/** Lua object receive creation. */
static int pdlua_receive_new (lua_State *L);
/** Lua object receive destruction. */
static int pdlua_receive_free (lua_State *L);
/** Lua object clock creation. */
static int pdlua_clock_new (lua_State *L);
/** Lua proxy clock delay. */
static int pdlua_clock_delay (lua_State *L);
/** Lua proxy clock set. */
static int pdlua_clock_set (lua_State *L);
/** Lua proxy clock unset. */
static int pdlua_clock_unset (lua_State *L);
/** Lua proxy clock destruction. */
static int pdlua_clock_free (lua_State *L);
/** Lua object destruction. */
static int pdlua_object_free (lua_State *L);
/** Dispatch Pd inlet messages to Lua objects. */
static void pdlua_dispatch (t_pdlua *o, unsigned int inlet, t_symbol *s, int argc, t_atom *argv);
/** Dispatch Pd receive messages to Lua objects. */
static void pdlua_receivedispatch (t_pdlua_proxyreceive *r, t_symbol *s, int argc, t_atom *argv);
/** Dispatch Pd clock messages to Lua objects. */
static void pdlua_clockdispatch(t_pdlua_proxyclock *clock);
/** Convert a Lua table into a Pd atom array. */
static t_atom *pdlua_popatomtable (lua_State *L, int *count, t_pdlua *o);
/** Send a message from a Lua object outlet. */
static int pdlua_outlet (lua_State *L);
/** Send a message from a Lua object to a Pd receiver. */
static int pdlua_send (lua_State *L);
/** Set a [value] object's value. */
static int pdlua_setvalue (lua_State *L);
/** Get a [value] object's value. */
static int pdlua_getvalue (lua_State *L);
/** Get a [table] object's array. */
static int pdlua_getarray (lua_State *L);
/** Read from a [table] object's array. */
static int pdlua_readarray (lua_State *L);
/** Write to a [table] object's array. */
static int pdlua_writearray (lua_State *L);
/** Redraw a [table] object's graph. */
static int pdlua_redrawarray (lua_State *L);
/** Post to Pd's console. */
static int pdlua_post (lua_State *L);
/** Report an error from a Lua object to Pd's console. */
static int pdlua_error (lua_State *L);
static void pdlua_setrequirepath (lua_State *L, const char *path);
static void pdlua_clearrequirepath (lua_State *L);
/** Run a Lua script using Pd's path. */
static int pdlua_dofile (lua_State *L);
/** Initialize the pd API for Lua. */
static void pdlua_init (lua_State *L);
/** Pd loader hook for loading and executing Lua scripts. */
static int pdlua_loader_legacy (t_canvas *canvas, char *name);
/** Start the Lua runtime and register our loader hook. */
#ifdef _WIN32
__declspec(dllexport)
#endif
#ifdef PLUGDATA
void pdlua_setup(const char *datadir, char *versbuf, int versbuf_length, void(*register_class_callback)(const char*));
#else
void pdlua_setup (void);
#endif
/* end prototypes*/
/* globals */
struct pdlua_proxyinlet;
struct pdlua_proxyreceive;
struct pdlua_proxyclock;
/** Proxy inlet class pointer. */
static t_class *pdlua_proxyinlet_class;
/** Proxy receive class pointer. */
static t_class *pdlua_proxyreceive_class;
/** Proxy clock class pointer. */
static t_class *pdlua_proxyclock_class;
/** Lua file reader callback. */
static const char *pdlua_reader
(
lua_State *UNUSED(L), /**< Lua interpreter state. */
void *rr, /**< Lua file reader state. */
size_t *size /**< How much data we have read. */
)
{
t_pdlua_readerdata *r = rr;
ssize_t s;
PDLUA_DEBUG("pdlua_reader: fd is %d", r->fd);
s = read(r->fd, r->buffer, MAXPDSTRING-2);
PDLUA_DEBUG("pdlua_reader: s is %ld", s);////////
if (s <= 0)
{
*size = 0;
return NULL;
}
else
{
*size = s;
return r->buffer;
}
}
/** Proxy inlet 'anything' method. */
static void pdlua_proxyinlet_anything
(
t_pdlua_proxyinlet *p, /**< The proxy inlet that received the message. */
t_symbol *s, /**< The message selector. */
int argc, /**< The message length. */
t_atom *argv /**< The atoms in the message. */
)
{
pdlua_dispatch(p->owner, p->id, s, argc, argv);
}
static void pdlua_proxyinlet_fwd
(
t_pdlua_proxyinlet *p, /**< The proxy inlet that received the message. */
t_symbol *UNUSED(s), /**< The message selector, which is always "fwd" */
int argc, /**< The message length. */
t_atom *argv /**< The atoms in the message. The first atom is the actual selector */
)
{
if(!argc) return;
pdlua_dispatch(p->owner, p->id, atom_getsymbol(argv), argc-1, argv+1);
}
/** Proxy inlet initialization. */
static void pdlua_proxyinlet_init
(
t_pdlua_proxyinlet *p, /**< The proxy inlet to initialize. */
struct pdlua *owner, /**< The owning object. */
unsigned int id /**< The inlet number. */
)
{
p->pd = pdlua_proxyinlet_class;
p->owner = owner;
p->id = id;
}
/** Register the proxy inlet class with Pd. */
static void pdlua_proxyinlet_setup(void)
{
pdlua_proxyinlet_class = class_new(gensym("pdlua proxy inlet"), 0, 0, sizeof(t_pdlua_proxyinlet), 0, 0);
if (pdlua_proxyinlet_class) {
class_addanything(pdlua_proxyinlet_class, pdlua_proxyinlet_anything);
class_addmethod(pdlua_proxyinlet_class, (t_method)pdlua_proxyinlet_fwd, gensym("fwd"), A_GIMME, 0);
}
}
/** Proxy receive 'anything' method. */
static void pdlua_proxyreceive_anything(
t_pdlua_proxyreceive *r, /**< The proxy receive that received the message. */
t_symbol *s, /**< The message selector. */
int argc, /**< The message length. */
t_atom *argv /**< The atoms in the message. */
)
{
pdlua_receivedispatch(r, s, argc, argv);
}
/** Proxy receive allocation and initialization. */
static t_pdlua_proxyreceive *pdlua_proxyreceive_new
(
struct pdlua *owner, /**< The owning object. */
t_symbol *name /**< The symbol to bind to. */
)
{
t_pdlua_proxyreceive *r = malloc(sizeof(t_pdlua_proxyreceive));
r->pd = pdlua_proxyreceive_class;
r->owner = owner;
r->name = name;
pd_bind(&r->pd, r->name);
return r;
}
/** Proxy receive cleanup and deallocation. */
static void pdlua_proxyreceive_free(t_pdlua_proxyreceive *r /**< The proxy receive to free. */)
{
pd_unbind(&r->pd, r->name);
r->pd = NULL;
r->owner = NULL;
r->name = NULL;
free(r);
}
/** Register the proxy receive class with Pd. */
static void pdlua_proxyreceive_setup()
{
pdlua_proxyreceive_class = class_new(gensym("pdlua proxy receive"), 0, 0, sizeof(t_pdlua_proxyreceive), 0, 0);
if (pdlua_proxyreceive_class)
class_addanything(pdlua_proxyreceive_class, pdlua_proxyreceive_anything);
}
/** Proxy clock 'bang' method. */
static void pdlua_proxyclock_bang(t_pdlua_proxyclock *c /**< The proxy clock that received the message. */)
{
pdlua_clockdispatch(c);
}
/** Proxy clock allocation and initialization. */
static t_pdlua_proxyclock *pdlua_proxyclock_new
(
struct pdlua *owner /**< The object to forward messages to. */
)
{
t_pdlua_proxyclock *c = malloc(sizeof(t_pdlua_proxyclock));
c->pd = pdlua_proxyclock_class;
c->owner = owner;
c->clock = clock_new(c, (t_method) pdlua_proxyclock_bang);
return c;
}
/** Register the proxy clock class with Pd. */
static void pdlua_proxyclock_setup(void)
{
pdlua_proxyclock_class = class_new(gensym("pdlua proxy clock"), 0, 0, sizeof(t_pdlua_proxyclock), 0, 0);
}
/** Dump an array of atoms into a Lua table. */
static void pdlua_pushatomtable
(
int argc, /**< The number of atoms in the array. */
t_atom *argv /**< The array of atoms. */
)
{
int i;
PDLUA_DEBUG("pdlua_pushatomtable: stack top %d", lua_gettop(__L()));
lua_newtable(__L());
for (i = 0; i < argc; ++i)
{
lua_pushnumber(__L(), i+1);
switch (argv[i].a_type)
{
case A_FLOAT:
lua_pushnumber(__L(), argv[i].a_w.w_float);
break;
case A_SYMBOL:
lua_pushstring(__L(), argv[i].a_w.w_symbol->s_name);
break;
case A_POINTER: /* FIXME: check experimentality */
lua_pushlightuserdata(__L(), argv[i].a_w.w_gpointer);
break;
default:
pd_error(NULL, "lua: zomg weasels!");
lua_pushnil(__L());
break;
}
lua_settable(__L(), -3);
}
PDLUA_DEBUG("pdlua_pushatomtable: end. stack top %d", lua_gettop(__L()));
}
static const char *basename(const char *name)
{
/* strip dir from name : */
const char *basenamep = strrchr(name, '/');
#ifdef _WIN32
if (!basenamep)
basenamep = strrchr(name, '\\');
#endif
if (!basenamep)
basenamep = name;
else basenamep++; /* strip last '/' */
return basenamep;
}
// ag 20240907: Improved Lua error reporting. Go to some lengths to get
// prettier error messages than what the Lua runtime system provides.
// This lets us report source locations in case there's no real Lua error,
// but we still want a well-formatted custom error message.
static char *src_info(lua_State *L, char *msg)
{
// fill in some Lua source location information
lua_Debug ar;
// locate the Lua stack frame with our function; we're looking for a Lua
// source which is *not* pd.lua
for (int i = 1; i < 10 && lua_getstack(L, i, &ar) && lua_getinfo(L, "S", &ar); ++i) {
const char *src = ar.source;
// cf. "The Debug Interface" in the Lua reference manual
if (*src == '@') src = basename(src+1);
if (strcmp(ar.what, "Lua") == 0 && strcmp(src, "pd.lua") != 0) {
snprintf(msg, MAXPDSTRING-1, "%s: %d", src, ar.linedefined);
return msg;
}
}
// fall back to just a bland 'lua' if we couldn't find any information
strcpy(msg, "lua");
return msg;
}
// Drop-in replacement for lua_error() which outputs directly to the Pd
// console (instead of taking the detour via stderr), and also fixes up the
// error message itself; in particular, it replaces the [string "filename"]
// source designations with something nice.
static void mylua_error (lua_State *L, t_pdlua *o, const char *descr)
// o may indicate the object which is the source of the error, if available,
// otherwise it must be NULL; descr, if not NULL, is to be added to the message
{
const char *err = lua_isstring(L, -1) ? lua_tostring(L, -1) : "unknown error";
// some sscanf magic to extract the real source name
char s[MAXPDSTRING]; int i;
if (sscanf(err, "[string \"%[^\"]\"]:%n", s, &i) <= 0) strcpy(s, "");
// send the message directly to the Pd console
if (descr) {
if (*s)
pd_error(o, "lua: %s: %s: %s", descr, s, err+i);
else
pd_error(o, "lua: %s: %s", descr, err);
} else {
if (*s)
pd_error(o, "lua: %s: %s", s, err+i);
else
pd_error(o, "lua: %s", err);
}
// we've handled the error, pop the error string
lua_pop(L, 1);
}
/** Pd object constructor. */
static t_pdlua *pdlua_new
(
t_symbol *s, /**< The construction message selector. */
int argc, /**< The construction message atom count. */
t_atom *argv /**< The construction message atoms. */
)
{
int i;
PDLUA_DEBUG("pdlua_new: s->s_name is %s", s->s_name);
for (i = 0; i < argc; ++i)
{
switch (argv[i].a_type)
{
case A_FLOAT:
PDLUA_DEBUG2("argv[%d]: %f", i, argv[i].a_w.w_float);
break;
case A_SYMBOL:
PDLUA_DEBUG2("argv[%d]: %s", i, argv[i].a_w.w_symbol->s_name);
break;
default:
pd_error(NULL, "pdlua_new: bad argument type"); // should never happen
return NULL;
}
}
PDLUA_DEBUG("pdlua_new: start with stack top %d", lua_gettop(__L()));
lua_getglobal(__L(), "pd");
lua_getfield(__L(), -1, "_checkbase");
lua_pushstring(__L(), s->s_name);
lua_pcall(__L(), 1, 1, 0);
int needs_base = lua_toboolean(__L(), -1);
lua_pop(__L(), 1); /* pop boolean */
/* have to load the .pd_lua file for basename if another class is owning it */
if(needs_base) {
char buf[MAXPDSTRING];
char *ptr;
t_pdlua_readerdata reader;
t_canvas* current = canvas_getcurrent();
int fd = canvas_open(current, s->s_name, ".pd_lua", buf, &ptr, MAXPDSTRING, 1);
if (fd >= 0)
{
PDLUA_DEBUG("basename open: stack top %d", lua_gettop(__L()));
/* save old loadname, restore later in case of
* nested loading */
int n, load_name_save, load_path_save;
lua_getfield(__L(), -1, "_loadname");
load_name_save = luaL_ref(__L(), LUA_REGISTRYINDEX);
lua_pushnil(__L());
lua_setfield(__L(), -2, "_loadname");
lua_getfield(__L(), -1, "_loadpath");
load_path_save = luaL_ref(__L(), LUA_REGISTRYINDEX);
lua_pushstring(__L(), buf);
lua_setfield(__L(), -2, "_loadpath");
PDLUA_DEBUG("pdlua_new (basename load) path is %s", buf);
//pdlua_setpathname(o, buf);/* change the scriptname to include its path
pdlua_setrequirepath(__L(), buf);
class_set_extern_dir(gensym(buf));
strncpy(buf, s->s_name, MAXPDSTRING - 8);
strcat(buf, ".pd_lua");
reader.fd = fd;
n = lua_gettop(__L());
#if LUA_VERSION_NUM < 502
if (lua_load(__L(), pdlua_reader, &reader, buf))
#else // 5.2 style
if (lua_load(__L(), pdlua_reader, &reader, buf, NULL))
#endif // LUA_VERSION_NUM < 502
{
close(fd);
pdlua_clearrequirepath(__L());
mylua_error(__L(), NULL, NULL);
}
else
{
if (lua_pcall(__L(), 0, LUA_MULTRET, 0))
{
mylua_error(__L(), NULL, NULL);
close(fd);
pdlua_clearrequirepath(__L());
}
else
{
/* succeeded */
close(fd);
pdlua_clearrequirepath(__L());
}
}
class_set_extern_dir(&s_);
lua_settop(__L(), n); /* discard results of load */
lua_rawgeti(__L(), LUA_REGISTRYINDEX, load_path_save);
lua_setfield(__L(), -2, "_loadpath");
luaL_unref(__L(), LUA_REGISTRYINDEX, load_path_save);
lua_rawgeti(__L(), LUA_REGISTRYINDEX, load_name_save);
lua_setfield(__L(), -2, "_loadname");
luaL_unref(__L(), LUA_REGISTRYINDEX, load_name_save);
}
else pd_error(NULL, "lua: constructor: couldn't locate `%s'", buf);
}
PDLUA_DEBUG("pdlua_new: after load script. stack top %d", lua_gettop(__L()));
lua_getfield(__L(), -1, "_constructor");
lua_pushstring(__L(), s->s_name);
pdlua_pushatomtable(argc, argv);
PDLUA_DEBUG("pdlua_new: before lua_pcall(L, 2, 1, 0) stack top %d", lua_gettop(__L()));
if (lua_pcall(__L(), 2, 1, 0))
{
mylua_error(__L(), NULL, "constructor");
lua_pop(__L(), 1); /* pop the global "pd" */
return NULL;
}
else
{
t_pdlua *object = NULL;
PDLUA_DEBUG("pdlua_new: done lua_pcall(L, 2, 1, 0) stack top %d", lua_gettop(__L()));
if (lua_islightuserdata(__L(), -1))
{
object = lua_touserdata(__L(), -1);
lua_pop(__L(), 2);/* pop the userdata and the global "pd" */
PDLUA_DEBUG2("pdlua_new: before returning object %p stack top %d", object, lua_gettop(__L()));
return object;
}
else
{
lua_pop(__L(), 2);/* pop the userdata and the global "pd" */
PDLUA_DEBUG("pdlua_new: done FALSE lua_islightuserdata(L, -1)", 0);
return NULL;
}
}
}
/** Pd object destructor. */
static void pdlua_free( t_pdlua *o /**< The object to destruct. */)
{
PDLUA_DEBUG("pdlua_free: stack top %d", lua_gettop(__L()));
lua_getglobal(__L(), "pd");
lua_getfield (__L(), -1, "_destructor");
lua_pushlightuserdata(__L(), o);
if (lua_pcall(__L(), 1, 0, 0))
{
mylua_error(__L(), NULL, "destructor");
}
lua_pop(__L(), 1); /* pop the global "pd" */
PDLUA_DEBUG("pdlua_free: end. stack top %d", lua_gettop(__L()));
// Collect garbage
// If we don't do this here, it could potentially leak if no other pdlua objects are used afterwards
lua_gc(__L(), LUA_GCCOLLECT);
return;
}
void pdlua_vis(t_gobj *z, t_glist *glist, int vis){
t_pdlua* x = (t_pdlua *)z;
// If there's no gui, use default text vis behavior
if(!x->has_gui)
{
text_widgetbehavior.w_visfn(z, glist, vis);
return;
}
// Otherwise, repaint or clear the custom graphics
if(vis)
{
pdlua_gfx_repaint(x, 1);
}
else {
pdlua_gfx_clear(x, -1, 1);
}
}
static void pdlua_delete(t_gobj *z, t_glist *glist){
if(!((t_pdlua *)z)->has_gui)
{
text_widgetbehavior.w_deletefn(z, glist);
return;
}
if(glist_isvisible(glist) && gobj_shouldvis(z, glist)) {
pdlua_vis(z, glist, 0);
}
canvas_deletelinesfor(glist, (t_text *)z);
}
#ifdef PURR_DATA // Purr Data uses an older version of this API
static void pdlua_motion(t_gobj *z, t_floatarg dx, t_floatarg dy)
#else
static void pdlua_motion(t_gobj *z, t_floatarg dx, t_floatarg dy,
t_floatarg up)
#endif
{
#if !PLUGDATA
#ifndef PURR_DATA
if (!up)
#endif
{
t_pdlua *x = (t_pdlua *)z;
x->gfx.mouse_drag_x = x->gfx.mouse_drag_x + dx;
x->gfx.mouse_drag_y = x->gfx.mouse_drag_y + dy;
int zoom = glist_getzoom(glist_getcanvas(x->canvas));
int xpos = (x->gfx.mouse_drag_x - text_xpix(&x->pd, x->canvas)) / zoom;
int ypos = (x->gfx.mouse_drag_y - text_ypix(&x->pd, x->canvas)) / zoom;
pdlua_gfx_mouse_drag(x, xpos, ypos);
}
#endif
}
static int pdlua_click(t_gobj *z, t_glist *gl, int xpos, int ypos, int shift, int alt, int dbl, int doit){
t_pdlua *x = (t_pdlua *)z;
#if !PLUGDATA
if(x->has_gui)
{
int zoom = glist_getzoom(gl);
int xpix = (xpos - text_xpix(&x->pd, gl)) / zoom;
int ypix = (ypos - text_ypix(&x->pd, gl)) / zoom;
if(doit){
if(!x->gfx.mouse_down)
{
pdlua_gfx_mouse_down(x, xpix, ypix);
x->gfx.mouse_drag_x = xpos;
x->gfx.mouse_drag_y = ypos;
}
glist_grab(x->canvas, &x->pd.te_g, (t_glistmotionfn)pdlua_motion, NULL, xpos, ypos);
}
else {
pdlua_gfx_mouse_move(x, xpix, ypix);
if(x->gfx.mouse_down)
{
pdlua_gfx_mouse_up(x, xpix, ypix);
}
}
x->gfx.mouse_down = doit;
return 1;
} else
#endif
return text_widgetbehavior.w_clickfn(z, gl, xpos, ypos, shift, alt, dbl, doit);
}
static void pdlua_displace(t_gobj *z, t_glist *glist, int dx, int dy){
t_pdlua *x = (t_pdlua *)z;
if(x->has_gui)
{
x->pd.te_xpix += dx, x->pd.te_ypix += dy;
dx *= glist_getzoom(glist), dy *= glist_getzoom(glist);
#if !PLUGDATA
gfx_displace((t_pdlua*)z, glist, dx, dy);
#endif
}
else {
text_widgetbehavior.w_displacefn(z, glist, dx, dy);
}
canvas_fixlinesfor(glist, (t_text*)x);
}
#ifdef PURR_DATA
static void pdlua_displace_wtag(t_gobj *z, t_glist *glist, int dx, int dy){
t_pdlua *x = (t_pdlua *)z;
if(x->has_gui)
{
x->pd.te_xpix += dx, x->pd.te_ypix += dy;
//gfx_displace((t_pdlua*)z, glist, dx, dy);
}
else {
text_widgetbehavior.w_displacefnwtag(z, glist, dx, dy);
}
canvas_fixlinesfor(glist, (t_text*)x);
}
static void pdlua_select(t_gobj *z, t_glist *glist, int state)
{
t_pdlua *x = (t_pdlua *)z;
t_pdlua_gfx *gfx = &x->gfx;
t_canvas *cnv = glist_getcanvas(glist);
if(x->has_gui) {
if (gobj_shouldvis(&x->pd.te_g, glist)) {
if (state) {
gui_vmess("gui_gobj_select", "xs", cnv, gfx->object_tag);
} else {
gui_vmess("gui_gobj_deselect", "xs", cnv, gfx->object_tag);
}
}
} else {
text_widgetbehavior.w_selectfn(z, glist, state);
}
}
#endif
static void pdlua_activate(t_gobj *z, t_glist *glist, int state)
{
if(!((t_pdlua *)z)->has_gui)
{
// Bypass to text widgetbehaviour if we're not a GUI
text_widgetbehavior.w_activatefn(z, glist, state);
}
}
static void pdlua_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2)
{
t_pdlua *x = (t_pdlua *)z;
if(x->has_gui) {
#ifndef PURR_DATA
int zoom = glist->gl_zoom;
#else
int zoom = 1;
#endif
float x1 = text_xpix((t_text *)x, glist), y1 = text_ypix((t_text *)x, glist);
*xp1 = x1;
*yp1 = y1;
*xp2 = x1 + x->gfx.width * zoom;
*yp2 = y1 + x->gfx.height * zoom;
}
else {
// Bypass to text widgetbehaviour if we're not a GUI
text_widgetbehavior.w_getrectfn(z, glist, xp1, yp1, xp2, yp2);
}
}
#if 0
static void pdlua_stack_dump (lua_State *L)
{
int i;
int top = lua_gettop(L);
for (i = 1; i <= top; i++)
{ /* repeat for each level */
int t = lua_type(L, i);
switch (t)
{
case LUA_TSTRING: /* strings */
printf("`%s'", lua_tostring(L, i));
break;
case LUA_TBOOLEAN: /* booleans */
printf(lua_toboolean(L, i) ? "true" : "false");
break;
case LUA_TNUMBER: /* numbers */
printf("%g", lua_tonumber(L, i));
break;
default: /* other values */
printf("%s", lua_typename(L, t));
break;
}
printf(" "); /* put a separator */
}
printf("\n"); /* end the listing */
}
#endif
#ifdef WIN32
#define realpath(N,R) _fullpath((R),(N),PATH_MAX)
#endif
/* nw.js support. If this is non-NULL then we're running inside Jonathan
Wilkes' Pd-L2Ork variant and access to the GUI uses JavaScript. */
static void (*nw_gui_vmess)(const char *sel, char *fmt, ...) = NULL;
/* plugdata support. Similarly, if we're running inside plugdata, we can send GUI messages with plugdata_forward_message
This allows opening an in-gui text editor instead of opening another app
*/
#if PLUGDATA
void plugdata_forward_message(void* x, t_symbol *s, int argc, t_atom *argv);
#endif
/** a handler for the open item in the right-click menu (mrpeach 20111025) */
/** Here we find the lua code for the object and open it in an editor */
static void pdlua_menu_open(t_pdlua *o)
{
const char *name;
const char *path;
char pathname[FILENAME_MAX];
t_class *class;
/* 20240903 ag: This is surpringly complicated, because there are various
cases to consider. First, whoami gives us the script name, and
externdir its path. In rare cases the path may be relative to
pdlua_cwd, we account for that here. In most cases this will give us
the absolute pathname of the script. The only exception is pdluax,
where the name already includes the full path, so we just use that. */
PDLUA_DEBUG("pdlua_menu_open stack top is %d", lua_gettop(__L()));
/** Get the scriptname of the object */
lua_getglobal(__L(), "pd");
lua_getfield(__L(), -1, "_whoami");
lua_pushlightuserdata(__L(), o);
if (lua_pcall(__L(), 1, 1, 0))
{
mylua_error(__L(), NULL, "whoami");
lua_pop(__L(), 1); /* pop the global "pd" */