bd14e6c58e23de2a1f74ba1175effd3b787a32dd
[blender.git] / source / blender / blenkernel / intern / blender.c
1
2 /*  blender.c   jan 94     MIXED MODEL
3  * 
4  * common help functions and data
5  * 
6  * $Id$
7  *
8  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version. The Blender
14  * Foundation also sells licenses for use in proprietary software under
15  * the Blender License.  See http://www.blender.org/BL/ for information
16  * about this.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  *
27  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
28  * All rights reserved.
29  *
30  * The Original Code is: all of this file.
31  *
32  * Contributor(s): none yet.
33  *
34  * ***** END GPL/BL DUAL LICENSE BLOCK *****
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #ifndef WIN32 
42     #include <unistd.h> // for read close
43     #include <sys/param.h> // for MAXPATHLEN
44 #else
45     #include <io.h> // for open close read
46 #endif
47
48 #include <stdlib.h>
49 #include <stdio.h>
50 #include <string.h>
51 #include <fcntl.h> // for open
52
53 #include "MEM_guardedalloc.h"
54 #include "DNA_listBase.h"
55 #include "DNA_sdna_types.h"
56 #include "DNA_userdef_types.h"
57 #include "DNA_object_types.h"
58 #include "DNA_curve_types.h"
59
60 #include "BLI_blenlib.h"
61 #include "BLI_dynstr.h"
62
63 #include "IMB_imbuf_types.h"
64 #include "IMB_imbuf.h"
65
66 #ifdef WIN32
67 #include "BLI_winstuff.h"
68 #endif
69
70 #include "DNA_mesh_types.h"
71 #include "DNA_screen_types.h"
72
73 #include "BKE_library.h"
74 #include "BKE_blender.h"
75 #include "BKE_displist.h"
76 #include "BKE_global.h"
77 #include "BKE_main.h"
78 #include "BKE_object.h"
79 #include "BKE_scene.h"
80 #include "BKE_curve.h"
81 #include "BKE_font.h"
82
83 #include "BLI_editVert.h"
84
85 #include "BLO_undofile.h"
86 #include "BLO_readfile.h" 
87 #include "BLO_writefile.h" 
88
89 #include "BKE_bad_level_calls.h" // for freeAllRad editNurb free_editMesh free_editText free_editArmature
90 #include "BKE_utildefines.h" // O_BINARY FALSE
91 #include "BIF_mainqueue.h" // mainqenter for onload script
92 #include "mydevice.h"
93 #include "nla.h"
94
95 Global G;
96 UserDef U;
97
98 char versionstr[48]= "";
99
100 /* ************************************************ */
101 /* pushpop facility: to store data temporally, FIFO! */
102
103 ListBase ppmain={0, 0};
104
105 typedef struct PushPop {
106         struct PushPop *next, *prev;
107         void *data;
108         int len;
109 } PushPop;
110
111 void pushdata(void *data, int len)
112 {
113         PushPop *pp;
114         
115         pp= MEM_mallocN(sizeof(PushPop), "pushpop");
116         BLI_addtail(&ppmain, pp);
117         pp->data= MEM_mallocN(len, "pushpop");
118         pp->len= len;
119         memcpy(pp->data, data, len);
120 }
121
122 void popfirst(void *data)
123 {
124         PushPop *pp;
125         
126         pp= ppmain.first;
127         if(pp) {
128                 memcpy(data, pp->data, pp->len);
129                 BLI_remlink(&ppmain, pp);
130                 MEM_freeN(pp->data);
131                 MEM_freeN(pp);
132         }
133         else printf("error in popfirst\n");
134 }
135
136 void poplast(void *data)
137 {
138         PushPop *pp;
139         
140         pp= ppmain.last;
141         if(pp) {
142                 memcpy(data, pp->data, pp->len);
143                 BLI_remlink(&ppmain, pp);
144                 MEM_freeN(pp->data);
145                 MEM_freeN(pp);
146         }
147         else printf("error in poplast\n");
148 }
149
150 void free_pushpop()
151 {
152         PushPop *pp;
153
154         pp= ppmain.first;
155         while(pp) {
156                 BLI_remlink(&ppmain, pp);
157                 MEM_freeN(pp->data);
158                 MEM_freeN(pp);
159         }       
160 }
161
162 void pushpop_test()
163 {
164         if(ppmain.first) printf("pushpop not empty\n");
165         free_pushpop();
166 }
167
168
169
170 /* ********** free ********** */
171
172 void free_blender(void)
173 {
174         free_main(G.main);
175         G.main= NULL;
176
177         IMB_freeImBufdata();            /* imbuf lib */
178 }
179
180 void duplicatelist(ListBase *list1, ListBase *list2)  /* copy from 2 to 1 */
181 {
182         struct Link *link1, *link2;
183         
184         list1->first= list1->last= 0;
185         
186         link2= list2->first;
187         while(link2) {
188
189                 link1= MEM_dupallocN(link2);
190                 BLI_addtail(list1, link1);
191                 
192                 link2= link2->next;
193         }       
194 }
195
196 static EditMesh theEditMesh;
197
198 void initglobals(void)
199 {
200         memset(&G, 0, sizeof(Global));
201         
202         G.editMesh = &theEditMesh;
203         memset(G.editMesh, 0, sizeof(G.editMesh));
204
205         U.savetime= 1;
206
207         G.animspeed= 4;
208
209         G.main= MEM_callocN(sizeof(Main), "initglobals");
210
211         strcpy(G.ima, "//");
212
213         G.version= BLENDER_VERSION;
214
215         G.order= 1;
216         G.order= (((char*)&G.order)[0])?L_ENDIAN:B_ENDIAN;
217
218         sprintf(versionstr, "www.blender.org %d", G.version);
219
220 #ifdef _WIN32   // FULLSCREEN
221         G.windowstate = G_WINDOWSTATE_USERDEF;
222 #endif
223
224         clear_workob(); /* object.c */
225 }
226
227 /***/
228
229 static void clear_global(void) 
230 {
231         extern short winqueue_break;    /* screen.c */
232
233         freeAllRad();
234         free_main(G.main); /* free all lib data */
235         freefastshade();        /* othwerwise old lamp settings stay active */
236
237
238         /* prevent hanging vars */      
239         R.backbuf= 0;
240         
241         /* force all queues to be left */
242         winqueue_break= 1;
243         
244         if (G.obedit) {
245                 freeNurblist(&editNurb);
246                 free_editMesh(G.editMesh);
247                 free_editText();
248                 free_editArmature();
249         }
250
251         G.curscreen= NULL;
252         G.scene= NULL;
253         G.main= NULL;
254         
255         G.obedit= NULL;
256         G.obpose= NULL;
257         G.saction= NULL;
258         G.buts= NULL;
259         G.v2d= NULL;
260         G.vd= NULL;
261         G.soops= NULL;
262         G.sima= NULL;
263         G.sipo= NULL;
264         
265         G.f &= ~(G_WEIGHTPAINT + G_VERTEXPAINT + G_FACESELECT);
266 }
267
268 static void setup_app_data(BlendFileData *bfd, char *filename) 
269 {
270         Object *ob;
271         Base *base;
272         bScreen *curscreen= NULL;
273         Scene *curscene= NULL;
274         char mode;
275         
276         /* 'u' = undo save, 'n' = no UI load */
277         if(bfd->main->screen.first==NULL) mode= 'u';
278         else if(G.fileflags & G_FILE_NO_UI) mode= 'n';
279         else mode= 0;
280         
281         /* no load screens? */
282         if(mode) {
283                 /* comes from readfile.c */
284                 extern void lib_link_screen_restore(Main *, char, Scene *);
285                 
286                 SWAP(ListBase, G.main->screen, bfd->main->screen);
287                 
288                 /* we re-use current screen */
289                 curscreen= G.curscreen;
290                 /* but use new Scene pointer */
291                 curscene= bfd->curscene;
292                 if(curscene==NULL) curscene= bfd->main->scene.first;
293                 /* and we enforce curscene to be in current screen */
294                 curscreen->scene= curscene;
295
296                 /* clear_global will free G.main, here we can still restore pointers */
297                 lib_link_screen_restore(bfd->main, mode, curscene);
298         }
299         
300         clear_global();
301         
302         if(mode!='u') G.save_over = 1;
303         
304         G.main= bfd->main;
305         if (bfd->user) {
306                 U= *bfd->user;
307                 MEM_freeN(bfd->user);
308                 
309                 /* the UserDef struct is not corrected with do_versions() .... ugh! */
310                 if(U.wheellinescroll == 0) U.wheellinescroll = 3;
311                 if(U.menuthreshold1==0) {
312                         U.menuthreshold1= 5;
313                         U.menuthreshold2= 2;
314                 }
315                 if(U.tb_leftmouse==0) {
316                         U.tb_leftmouse= 5;
317                         U.tb_rightmouse= 5;
318                 }
319                 if(U.mixbufsize==0) U.mixbufsize= 2048;
320         }
321         
322         /* case G_FILE_NO_UI or no screens in file */
323         if(mode) {
324                 G.curscreen= curscreen;
325                 G.scene= curscene;
326         }
327         else {
328                 R.winpos= bfd->winpos;
329                 R.displaymode= bfd->displaymode;
330                 G.fileflags= bfd->fileflags;
331                 G.curscreen= bfd->curscreen;
332                 G.scene= G.curscreen->scene;
333         }
334         
335         /* special cases, override loaded flags: */
336         if (G.f & G_DEBUG) bfd->globalf |= G_DEBUG;
337         else bfd->globalf &= ~G_DEBUG;
338         if (G.f & G_SCENESCRIPT) bfd->globalf |= G_SCENESCRIPT;
339         else bfd->globalf &= ~G_SCENESCRIPT;
340
341         G.f= bfd->globalf;
342         
343         /* check posemode */
344         for(base= G.scene->base.first; base; base=base->next) {
345                 ob= base->object;
346                 if(ob->flag & OB_POSEMODE) {
347                         if(ob->type==OB_ARMATURE) G.obpose= ob;
348                 }
349         }
350         
351         /* few DispLists, but do text_to_curve */
352         // this should be removed!!! But first a better displist system (ton)
353         for (ob= G.main->object.first; ob; ob= ob->id.next) {
354                 if(ob->type==OB_FONT) {
355                         Curve *cu= ob->data;
356                         if(cu->nurb.first==0) text_to_curve(ob, 0);
357                 }
358         }
359         
360         if (!G.background) {
361                 setscreen(G.curscreen);
362         }
363                 /* baseflags */
364         set_scene_bg(G.scene);
365
366         if (G.f & G_SCENESCRIPT) {
367                 /* there's an onload scriptlink to execute in screenmain */
368                 mainqenter(ONLOAD_SCRIPT, 1);
369         }
370
371         strcpy(G.sce, filename);
372         strcpy(G.main->name, filename); /* is guaranteed current file */
373         
374         MEM_freeN(bfd);
375 }
376
377 int BKE_read_file(char *dir, void *type_r) 
378 {
379         BlendReadError bre;
380         BlendFileData *bfd;
381         
382         if (!G.background)
383                 waitcursor(1);
384                 
385         bfd= BLO_read_from_file(dir, &bre);
386         if (bfd) {
387                 if (type_r)
388                         *((BlenFileType*)type_r)= bfd->type;
389                 
390                 setup_app_data(bfd, dir);
391         } else {
392                 error("Loading %s failed: %s", dir, BLO_bre_as_string(bre));
393         }
394         
395         if (!G.background)
396                 waitcursor(0);
397         
398         return (bfd?1:0);
399 }
400
401 int BKE_read_file_from_memory(char* filebuf, int filelength, void *type_r)
402 {
403         BlendReadError bre;
404         BlendFileData *bfd;
405         
406         if (!G.background)
407                 waitcursor(1);
408                 
409         bfd= BLO_read_from_memory(filebuf, filelength, &bre);
410         if (bfd) {
411                 if (type_r)
412                         *((BlenFileType*)type_r)= bfd->type;
413                 
414                 setup_app_data(bfd, "<memory>");
415         } else {
416                 error("Loading failed: %s", BLO_bre_as_string(bre));
417         }
418         
419         if (!G.background)
420                 waitcursor(0);
421         
422         return (bfd?1:0);
423 }
424
425 int BKE_read_file_from_memfile(MemFile *memfile)
426 {
427         BlendReadError bre;
428         BlendFileData *bfd;
429         
430         if (!G.background)
431                 waitcursor(1);
432                 
433         bfd= BLO_read_from_memfile(memfile, &bre);
434         if (bfd) {
435                 setup_app_data(bfd, "<memory>");
436         } else {
437                 error("Loading failed: %s", BLO_bre_as_string(bre));
438         }
439         
440         if (!G.background)
441                 waitcursor(0);
442         
443         return (bfd?1:0);
444 }
445
446
447 /* ***************** GLOBAL UNDO *************** */
448
449 #define UNDO_DISK       0
450
451 #define MAXUNDONAME     64
452 typedef struct UndoElem {
453         struct UndoElem *next, *prev;
454         char str[FILE_MAXDIR+FILE_MAXFILE];
455         char name[MAXUNDONAME];
456         MemFile memfile;
457 } UndoElem;
458
459 #define MAXUNDO  32
460 static ListBase undobase={NULL, NULL};
461 static UndoElem *curundo= NULL;
462
463
464 static int read_undosave(UndoElem *uel)
465 {
466         char scestr[FILE_MAXDIR+FILE_MAXFILE];
467         int success=0, fileflags;
468         
469         strcpy(scestr, G.sce);  /* temporal store */
470         fileflags= G.fileflags;
471         G.fileflags |= G_FILE_NO_UI;
472
473         if(UNDO_DISK) 
474                 success= BKE_read_file(uel->str, NULL);
475         else
476                 success= BKE_read_file_from_memfile(&uel->memfile);
477         
478         /* restore */
479         strcpy(G.sce, scestr);
480         G.fileflags= fileflags;
481
482         return success;
483 }
484
485 /* name can be a dynamic string */
486 void BKE_write_undo(char *name)
487 {
488         int nr, success;
489         UndoElem *uel;
490         
491         if( (U.uiflag & USER_GLOBALUNDO)==0) return;
492
493         /* remove all undos after (also when curundo==NULL) */
494         while(undobase.last != curundo) {
495                 uel= undobase.last;
496                 BLI_remlink(&undobase, uel);
497                 BLO_free_memfile(&uel->memfile);
498                 MEM_freeN(uel);
499         }
500         
501         /* make new */
502         curundo= uel= MEM_callocN(sizeof(UndoElem), "undo file");
503         strncpy(uel->name, name, MAXUNDONAME-1);
504         BLI_addtail(&undobase, uel);
505         
506         /* and limit amount to the maximum */
507         nr= 0;
508         uel= undobase.last;
509         while(uel) {
510                 nr++;
511                 if(nr==MAXUNDO) break;
512                 uel= uel->prev;
513         }
514         if(uel) {
515                 while(undobase.first!=uel) {
516                         UndoElem *first= undobase.first;
517                         BLI_remlink(&undobase, first);
518                         /* the merge is because of compression */
519                         BLO_merge_memfile(&first->memfile, &first->next->memfile);
520                         MEM_freeN(first);
521                 }
522         }
523
524
525         /* disk save version */
526         if(UNDO_DISK) {
527                 static int counter= 0;
528                 char *err, tstr[FILE_MAXDIR+FILE_MAXFILE];
529                 char numstr[32];
530                 
531                 /* calculate current filename */
532                 counter++;
533                 counter= counter % MAXUNDO;     
534         
535                 sprintf(numstr, "%d.blend", counter);
536                 BLI_make_file_string("/", tstr, U.tempdir, numstr);
537         
538                 success= BLO_write_file(tstr, G.fileflags, &err);
539                 
540                 strcpy(curundo->str, tstr);
541         }
542         else {
543                 MemFile *prevfile=NULL;
544                 char *err;
545                 
546                 if(curundo->prev) prevfile= &(curundo->prev->memfile);
547                 
548                 success= BLO_write_file_mem(prevfile, &curundo->memfile, G.fileflags, &err);
549                 
550         }
551 }
552
553 /* 1= an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */
554 void BKE_undo_step(int step)
555 {
556         
557         if(step==0) {
558                 read_undosave(curundo);
559         }
560         else if(step==1) {
561                 /* curundo should never be NULL, after restart or load file it should call undo_save */
562                 if(curundo==NULL || curundo->prev==NULL) error("No undo available");
563                 else {
564                         if(G.f & G_DEBUG) printf("undo %s\n", curundo->name);
565                         curundo= curundo->prev;
566                         read_undosave(curundo);
567                 }
568         }
569         else {
570                 
571                 /* curundo has to remain current situation! */
572                 
573                 if(curundo==NULL || curundo->next==NULL) error("No redo available");
574                 else {
575                         read_undosave(curundo->next);
576                         curundo= curundo->next;
577                         if(G.f & G_DEBUG) printf("redo %s\n", curundo->name);
578                 }
579         }
580 }
581
582 void BKE_reset_undo(void)
583 {
584         UndoElem *uel;
585         
586         uel= undobase.first;
587         while(uel) {
588                 BLO_free_memfile(&uel->memfile);
589                 uel= uel->next;
590         }
591         
592         BLI_freelistN(&undobase);
593         curundo= NULL;
594 }
595
596 /* based on index nr it does a restore */
597 void BKE_undo_number(int nr)
598 {
599         UndoElem *uel;
600         int a=1;
601         
602         for(uel= undobase.first; uel; uel= uel->next, a++) {
603                 if(a==nr) break;
604         }
605         curundo= uel;
606         BKE_undo_step(0);
607 }
608
609 char *BKE_undo_menu_string(void)
610 {
611         UndoElem *uel;
612         DynStr *ds= BLI_dynstr_new();
613         char *menu;
614         
615         BLI_dynstr_append(ds, "Global Undo History %t");
616         
617         for(uel= undobase.first; uel; uel= uel->next) {
618                 BLI_dynstr_append(ds, "|");
619                 BLI_dynstr_append(ds, uel->name);
620         }
621         
622         menu= BLI_dynstr_get_cstring(ds);
623         BLI_dynstr_free(ds);
624
625         return menu;
626 }
627
628         /* saves quit.blend */
629 void BKE_undo_save_quit(void)
630 {
631         UndoElem *uel;
632         MemFileChunk *chunk;
633         int file;
634         char str[FILE_MAXDIR+FILE_MAXFILE];
635         
636         if( (U.uiflag & USER_GLOBALUNDO)==0) return;
637         
638         uel= curundo;
639         if(uel==NULL) {
640                 printf("No undo buffer to save recovery file\n");
641                 return;
642         }
643         
644         /* no undo state to save */
645         if(undobase.first==undobase.last) return;
646                 
647         BLI_make_file_string("/", str, U.tempdir, "quit.blend");
648
649         file = open(str,O_BINARY+O_WRONLY+O_CREAT+O_TRUNC, 0666);
650         if(file == -1) {
651                 printf("Unable to save %s\n", str);
652                 return;
653         }
654
655         chunk= uel->memfile.chunks.first;
656         while(chunk) {
657                 if( write(file, chunk->buf, chunk->size) != chunk->size) break;
658                 chunk= chunk->next;
659         }
660         
661         close(file);
662         
663         if(chunk) printf("Unable to save %s\n", str);
664         else printf("Saved session recovery to %s\n", str);
665 }
666