ce6d29bbfeea36e52ef63b16c01d7161d70beb39
[blender.git] / source / blender / blenkernel / intern / blender_undo.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/blenkernel/intern/blender_undo.c
22  *  \ingroup bke
23  *
24  * Blend file undo (known as 'Global Undo').
25  * DNA level diffing for undo.
26  */
27
28 #ifndef _WIN32
29 #  include <unistd.h> // for read close
30 #else
31 #  include <io.h> // for open close read
32 #endif
33
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <stddef.h>
37 #include <string.h>
38 #include <fcntl.h>  /* for open */
39 #include <errno.h>
40
41 #include "MEM_guardedalloc.h"
42
43 #include "DNA_scene_types.h"
44
45 #include "BLI_fileops.h"
46 #include "BLI_listbase.h"
47 #include "BLI_path_util.h"
48 #include "BLI_string.h"
49 #include "BLI_utildefines.h"
50
51 #include "IMB_imbuf.h"
52 #include "IMB_moviecache.h"
53
54 #include "BKE_blender_undo.h"  /* own include */
55 #include "BKE_blendfile.h"
56 #include "BKE_appdir.h"
57 #include "BKE_brush.h"
58 #include "BKE_context.h"
59 #include "BKE_depsgraph.h"
60 #include "BKE_global.h"
61 #include "BKE_image.h"
62 #include "BKE_main.h"
63 #include "RE_pipeline.h"
64
65 #include "BLO_undofile.h"
66 #include "BLO_readfile.h"
67 #include "BLO_writefile.h"
68
69 /* -------------------------------------------------------------------- */
70
71 /** \name Global Undo
72  * \{ */
73
74 #define UNDO_DISK   0
75
76 typedef struct UndoElem {
77         struct UndoElem *next, *prev;
78         char str[FILE_MAX];
79         char name[BKE_UNDO_STR_MAX];
80         MemFile memfile;
81         uintptr_t undosize;
82 } UndoElem;
83
84 static ListBase undobase = {NULL, NULL};
85 static UndoElem *curundo = NULL;
86
87 /**
88  * Avoid bad-level call to #WM_jobs_kill_all_except()
89  */
90 static void (*undo_wm_job_kill_callback)(struct bContext *C) = NULL;
91
92 void BKE_undo_callback_wm_kill_jobs_set(void (*callback)(struct bContext *C))
93 {
94         undo_wm_job_kill_callback = callback;
95 }
96
97 static int read_undosave(bContext *C, UndoElem *uel)
98 {
99         char mainstr[sizeof(G.main->name)];
100         int success = 0, fileflags;
101
102         /* This is needed so undoing/redoing doesn't crash with threaded previews going */
103         undo_wm_job_kill_callback(C);
104
105         BLI_strncpy(mainstr, G.main->name, sizeof(mainstr));    /* temporal store */
106
107         fileflags = G.fileflags;
108         G.fileflags |= G_FILE_NO_UI;
109
110         if (UNDO_DISK)
111                 success = (BKE_blendfile_read(C, uel->str, NULL) != BKE_BLENDFILE_READ_FAIL);
112         else
113                 success = BKE_blendfile_read_from_memfile(C, &uel->memfile, NULL);
114
115         /* restore */
116         BLI_strncpy(G.main->name, mainstr, sizeof(G.main->name)); /* restore */
117         G.fileflags = fileflags;
118
119         if (success) {
120                 /* important not to update time here, else non keyed tranforms are lost */
121                 DAG_on_visible_update(G.main, false);
122         }
123
124         return success;
125 }
126
127 /* name can be a dynamic string */
128 void BKE_undo_write(bContext *C, const char *name)
129 {
130         uintptr_t maxmem, totmem, memused;
131         int nr /*, success */ /* UNUSED */;
132         UndoElem *uel;
133
134         if ((U.uiflag & USER_GLOBALUNDO) == 0) {
135                 return;
136         }
137
138         if (U.undosteps == 0) {
139                 return;
140         }
141
142         /* remove all undos after (also when curundo == NULL) */
143         while (undobase.last != curundo) {
144                 uel = undobase.last;
145                 BLI_remlink(&undobase, uel);
146                 BLO_memfile_free(&uel->memfile);
147                 MEM_freeN(uel);
148         }
149
150         /* make new */
151         curundo = uel = MEM_callocN(sizeof(UndoElem), "undo file");
152         BLI_strncpy(uel->name, name, sizeof(uel->name));
153         BLI_addtail(&undobase, uel);
154
155         /* and limit amount to the maximum */
156         nr = 0;
157         uel = undobase.last;
158         while (uel) {
159                 nr++;
160                 if (nr == U.undosteps) break;
161                 uel = uel->prev;
162         }
163         if (uel) {
164                 while (undobase.first != uel) {
165                         UndoElem *first = undobase.first;
166                         BLI_remlink(&undobase, first);
167                         /* the merge is because of compression */
168                         BLO_memfile_merge(&first->memfile, &first->next->memfile);
169                         MEM_freeN(first);
170                 }
171         }
172
173
174         /* disk save version */
175         if (UNDO_DISK) {
176                 static int counter = 0;
177                 char filepath[FILE_MAX];
178                 char numstr[32];
179                 int fileflags = G.fileflags & ~(G_FILE_HISTORY); /* don't do file history on undo */
180
181                 /* calculate current filepath */
182                 counter++;
183                 counter = counter % U.undosteps;
184
185                 BLI_snprintf(numstr, sizeof(numstr), "%d.blend", counter);
186                 BLI_make_file_string("/", filepath, BKE_tempdir_session(), numstr);
187
188                 /* success = */ /* UNUSED */ BLO_write_file(CTX_data_main(C), filepath, fileflags, NULL, NULL);
189
190                 BLI_strncpy(curundo->str, filepath, sizeof(curundo->str));
191         }
192         else {
193                 MemFile *prevfile = NULL;
194
195                 if (curundo->prev) prevfile = &(curundo->prev->memfile);
196
197                 memused = MEM_get_memory_in_use();
198                 /* success = */ /* UNUSED */ BLO_write_file_mem(CTX_data_main(C), prevfile, &curundo->memfile, G.fileflags);
199                 curundo->undosize = MEM_get_memory_in_use() - memused;
200         }
201
202         if (U.undomemory != 0) {
203                 /* limit to maximum memory (afterwards, we can't know in advance) */
204                 totmem = 0;
205                 maxmem = ((uintptr_t)U.undomemory) * 1024 * 1024;
206
207                 /* keep at least two (original + other) */
208                 uel = undobase.last;
209                 while (uel && uel->prev) {
210                         totmem += uel->undosize;
211                         if (totmem > maxmem) break;
212                         uel = uel->prev;
213                 }
214
215                 if (uel) {
216                         if (uel->prev && uel->prev->prev)
217                                 uel = uel->prev;
218
219                         while (undobase.first != uel) {
220                                 UndoElem *first = undobase.first;
221                                 BLI_remlink(&undobase, first);
222                                 /* the merge is because of compression */
223                                 BLO_memfile_merge(&first->memfile, &first->next->memfile);
224                                 MEM_freeN(first);
225                         }
226                 }
227         }
228 }
229
230 /* 1 = an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */
231 void BKE_undo_step(bContext *C, int step)
232 {
233
234         if (step == 0) {
235                 read_undosave(C, curundo);
236         }
237         else if (step == 1) {
238                 /* curundo should never be NULL, after restart or load file it should call undo_save */
239                 if (curundo == NULL || curundo->prev == NULL) {
240                         // XXX error("No undo available");
241                 }
242                 else {
243                         if (G.debug & G_DEBUG) printf("undo %s\n", curundo->name);
244                         curundo = curundo->prev;
245                         read_undosave(C, curundo);
246                 }
247         }
248         else {
249                 /* curundo has to remain current situation! */
250
251                 if (curundo == NULL || curundo->next == NULL) {
252                         // XXX error("No redo available");
253                 }
254                 else {
255                         read_undosave(C, curundo->next);
256                         curundo = curundo->next;
257                         if (G.debug & G_DEBUG) printf("redo %s\n", curundo->name);
258                 }
259         }
260 }
261
262 void BKE_undo_reset(void)
263 {
264         UndoElem *uel;
265
266         uel = undobase.first;
267         while (uel) {
268                 BLO_memfile_free(&uel->memfile);
269                 uel = uel->next;
270         }
271
272         BLI_freelistN(&undobase);
273         curundo = NULL;
274 }
275
276 /* based on index nr it does a restore */
277 void BKE_undo_number(bContext *C, int nr)
278 {
279         curundo = BLI_findlink(&undobase, nr);
280         BKE_undo_step(C, 0);
281 }
282
283 /* go back to the last occurance of name in stack */
284 void BKE_undo_name(bContext *C, const char *name)
285 {
286         UndoElem *uel = BLI_rfindstring(&undobase, name, offsetof(UndoElem, name));
287
288         if (uel && uel->prev) {
289                 curundo = uel->prev;
290                 BKE_undo_step(C, 0);
291         }
292 }
293
294 /* name optional */
295 bool BKE_undo_is_valid(const char *name)
296 {
297         if (name) {
298                 UndoElem *uel = BLI_rfindstring(&undobase, name, offsetof(UndoElem, name));
299                 return uel && uel->prev;
300         }
301
302         return undobase.last != undobase.first;
303 }
304
305 /* get name of undo item, return null if no item with this index */
306 /* if active pointer, set it to 1 if true */
307 const char *BKE_undo_get_name(int nr, bool *r_active)
308 {
309         UndoElem *uel = BLI_findlink(&undobase, nr);
310
311         if (r_active) *r_active = false;
312
313         if (uel) {
314                 if (r_active && (uel == curundo)) {
315                         *r_active = true;
316                 }
317                 return uel->name;
318         }
319         return NULL;
320 }
321
322 /* return the name of the last item */
323 const char *BKE_undo_get_name_last()
324 {
325         UndoElem *uel = undobase.last;
326         return (uel ? uel->name : NULL);
327 }
328
329 /**
330  * Saves .blend using undo buffer.
331  *
332  * \return success.
333  */
334 bool BKE_undo_save_file(const char *filename)
335 {
336         UndoElem *uel;
337         MemFileChunk *chunk;
338         int file, oflags;
339
340         if ((U.uiflag & USER_GLOBALUNDO) == 0) {
341                 return false;
342         }
343
344         uel = curundo;
345         if (uel == NULL) {
346                 fprintf(stderr, "No undo buffer to save recovery file\n");
347                 return false;
348         }
349
350         /* note: This is currently used for autosave and 'quit.blend', where _not_ following symlinks is OK,
351          * however if this is ever executed explicitly by the user, we may want to allow writing to symlinks.
352          */
353
354         oflags = O_BINARY | O_WRONLY | O_CREAT | O_TRUNC;
355 #ifdef O_NOFOLLOW
356         /* use O_NOFOLLOW to avoid writing to a symlink - use 'O_EXCL' (CVE-2008-1103) */
357         oflags |= O_NOFOLLOW;
358 #else
359         /* TODO(sergey): How to deal with symlinks on windows? */
360 #  ifndef _MSC_VER
361 #    warning "Symbolic links will be followed on undo save, possibly causing CVE-2008-1103"
362 #  endif
363 #endif
364         file = BLI_open(filename,  oflags, 0666);
365
366         if (file == -1) {
367                 fprintf(stderr, "Unable to save '%s': %s\n",
368                         filename, errno ? strerror(errno) : "Unknown error opening file");
369                 return false;
370         }
371
372         for (chunk = uel->memfile.chunks.first; chunk; chunk = chunk->next) {
373                 if (write(file, chunk->buf, chunk->size) != chunk->size) {
374                         break;
375                 }
376         }
377
378         close(file);
379
380         if (chunk) {
381                 fprintf(stderr, "Unable to save '%s': %s\n",
382                         filename, errno ? strerror(errno) : "Unknown error writing file");
383                 return false;
384         }
385         return true;
386 }
387
388 /* sets curscene */
389 Main *BKE_undo_get_main(Scene **r_scene)
390 {
391         Main *mainp = NULL;
392         BlendFileData *bfd = BLO_read_from_memfile(G.main, G.main->name, &curundo->memfile, NULL);
393
394         if (bfd) {
395                 mainp = bfd->main;
396                 if (r_scene) {
397                         *r_scene = bfd->curscene;
398                 }
399
400                 MEM_freeN(bfd);
401         }
402
403         return mainp;
404 }
405
406 /** \} */