blend file thumbnailing
- uses same thumbnail system as image browser - blend files show thumbnails in ubuntu/gnome (freedesktop spec) - 128x128 images are embedded into the blend file header, a simple loader avoids reading the entire blend file to extract it when generating thumbnails in the file selector. When the image browser reads a directory it loads images and creates thumbnails, blend files embedded images are treated just like loading an image. - the thumbnail is created from the camera view in solid mode. (no camera == no thumbnal). - readfile/writefile.c: had to use the 'TEST' code name to save thumbnails, anything else would segfault older blender versions on load. (its not used elsewhere).
This commit is contained in:
@@ -166,7 +166,7 @@
|
||||
#define IMAG MAKE_ID('I','M','A','G')
|
||||
|
||||
#define DNA1 MAKE_ID('D','N','A','1')
|
||||
#define TEST MAKE_ID('T','E','S','T')
|
||||
#define TEST MAKE_ID('T','E','S','T') /* used as preview between 'REND' and 'GLOB' */
|
||||
#define REND MAKE_ID('R','E','N','D')
|
||||
#define USER MAKE_ID('U','S','E','R')
|
||||
|
||||
|
||||
@@ -534,7 +534,7 @@ void BKE_write_undo(bContext *C, char *name)
|
||||
sprintf(numstr, "%d.blend", counter);
|
||||
BLI_make_file_string("/", tstr, btempdir, numstr);
|
||||
|
||||
success= BLO_write_file(CTX_data_main(C), tstr, G.fileflags, NULL);
|
||||
success= BLO_write_file(CTX_data_main(C), tstr, G.fileflags, NULL, NULL);
|
||||
|
||||
strcpy(curundo->str, tstr);
|
||||
}
|
||||
|
||||
@@ -35,10 +35,12 @@ struct MemFile;
|
||||
struct Main;
|
||||
struct ReportList;
|
||||
|
||||
extern int BLO_write_file(struct Main *mainvar, char *dir, int write_flags, struct ReportList *reports);
|
||||
extern int BLO_write_file(struct Main *mainvar, char *dir, int write_flags, struct ReportList *reports, int *thumb);
|
||||
extern int BLO_write_file_mem(struct Main *mainvar, struct MemFile *compare, struct MemFile *current,
|
||||
int write_flags, struct ReportList *reports);
|
||||
extern int BLO_write_runtime(struct Main *mainvar, char *file, char *exename, struct ReportList *reports);
|
||||
|
||||
#define BLEN_THUMB_SIZE 128
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -10971,7 +10971,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filename)
|
||||
switch(bhead->code) {
|
||||
case DATA:
|
||||
case DNA1:
|
||||
case TEST:
|
||||
case TEST: /* used as preview since 2.5x */
|
||||
case REND:
|
||||
bhead = blo_nextbhead(fd, bhead);
|
||||
break;
|
||||
|
||||
@@ -62,6 +62,7 @@ Any case: direct data is ALWAYS after the lib block
|
||||
- write library block
|
||||
- per LibBlock
|
||||
- write the ID of LibBlock
|
||||
- write TEST (128x128, blend file preview, optional)
|
||||
- write FileGlobal (some global vars)
|
||||
- write SDNA
|
||||
- write USER if filename is ~/.B.blend
|
||||
@@ -2390,9 +2391,19 @@ static void write_global(WriteData *wd, int fileflags, Main *mainvar)
|
||||
writestruct(wd, GLOB, "FileGlobal", 1, &fg);
|
||||
}
|
||||
|
||||
/* preview image, first 2 values are width and height
|
||||
* second are an RGBA image (unsigned char)
|
||||
* note, this uses 'TEST' since new types will segfault on file load for older blender versions.
|
||||
*/
|
||||
static void write_thumb(WriteData *wd, int *img)
|
||||
{
|
||||
if(img)
|
||||
writedata(wd, TEST, (2 + img[0] * img[1]) * sizeof(int), img);
|
||||
}
|
||||
|
||||
/* if MemFile * there's filesave to memory */
|
||||
static int write_file_handle(Main *mainvar, int handle, MemFile *compare, MemFile *current,
|
||||
int write_user_block, int write_flags)
|
||||
int write_user_block, int write_flags, int *thumb)
|
||||
{
|
||||
BHead bhead;
|
||||
ListBase mainlist;
|
||||
@@ -2407,6 +2418,7 @@ static int write_file_handle(Main *mainvar, int handle, MemFile *compare, MemFil
|
||||
mywrite(wd, buf, 12);
|
||||
|
||||
write_renderinfo(wd, mainvar);
|
||||
write_thumb(wd, thumb);
|
||||
write_global(wd, write_flags, mainvar);
|
||||
|
||||
/* no UI save in undo */
|
||||
@@ -2458,7 +2470,7 @@ static int write_file_handle(Main *mainvar, int handle, MemFile *compare, MemFil
|
||||
}
|
||||
|
||||
/* return: success (1) */
|
||||
int BLO_write_file(Main *mainvar, char *dir, int write_flags, ReportList *reports)
|
||||
int BLO_write_file(Main *mainvar, char *dir, int write_flags, ReportList *reports, int *thumb)
|
||||
{
|
||||
char userfilename[FILE_MAXDIR+FILE_MAXFILE];
|
||||
char tempname[FILE_MAXDIR+FILE_MAXFILE+1];
|
||||
@@ -2497,7 +2509,7 @@ int BLO_write_file(Main *mainvar, char *dir, int write_flags, ReportList *report
|
||||
makeFilesRelative(dir, NULL); /* note, making relative to something OTHER then G.sce */
|
||||
|
||||
/* actual file writing */
|
||||
err= write_file_handle(mainvar, file, NULL,NULL, write_user_block, write_flags);
|
||||
err= write_file_handle(mainvar, file, NULL,NULL, write_user_block, write_flags, thumb);
|
||||
close(file);
|
||||
|
||||
/* rename/compress */
|
||||
@@ -2550,7 +2562,7 @@ int BLO_write_file_mem(Main *mainvar, MemFile *compare, MemFile *current, int wr
|
||||
{
|
||||
int err;
|
||||
|
||||
err= write_file_handle(mainvar, 0, compare, current, 0, write_flags);
|
||||
err= write_file_handle(mainvar, 0, compare, current, 0, write_flags, NULL);
|
||||
|
||||
if(err==0) return 1;
|
||||
return 0;
|
||||
@@ -2646,7 +2658,7 @@ int BLO_write_runtime(Main *mainvar, char *file, char *exename, ReportList *repo
|
||||
outfd= open(gamename, O_BINARY|O_WRONLY|O_CREAT|O_TRUNC, 0777);
|
||||
if (outfd != -1) {
|
||||
|
||||
write_file_handle(mainvar, outfd, NULL,NULL, 0, G.fileflags);
|
||||
write_file_handle(mainvar, outfd, NULL,NULL, 0, G.fileflags, NULL);
|
||||
|
||||
if (write(outfd, " ", 1) != 1) {
|
||||
BKE_report(reports, RPT_ERROR, "Unable to write to output file.");
|
||||
@@ -2732,7 +2744,7 @@ int BLO_write_runtime(Main *mainvar, char *file, char *exename, ReportList *repo
|
||||
|
||||
datastart= lseek(outfd, 0, SEEK_CUR);
|
||||
|
||||
write_file_handle(mainvar, outfd, NULL,NULL, 0, G.fileflags);
|
||||
write_file_handle(mainvar, outfd, NULL,NULL, 0, G.fileflags, NULL);
|
||||
|
||||
if (!handle_write_msb_int(outfd, datastart) || (write(outfd, "BRUNTIME", 8)!=8)) {
|
||||
BKE_report(reports, RPT_ERROR, "Unable to write to output file.");
|
||||
|
||||
@@ -1280,6 +1280,8 @@ static void thumbnails_startjob(void *tjv, short *stop, short *do_update)
|
||||
while ( (*stop==0) && (limg) ) {
|
||||
if ( limg->flags & IMAGEFILE ) {
|
||||
limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_IMAGE);
|
||||
} else if ( limg->flags & BLENDERFILE ) {
|
||||
limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_BLEND);
|
||||
} else if ( limg->flags & MOVIEFILE ) {
|
||||
limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_MOVIE);
|
||||
if (!limg->img) {
|
||||
@@ -1334,7 +1336,7 @@ void thumbnails_start(struct FileList* filelist, const struct bContext* C)
|
||||
tj->filelist = filelist;
|
||||
for (idx = 0; idx < filelist->numfiles;idx++) {
|
||||
if (!filelist->filelist[idx].image) {
|
||||
if ( (filelist->filelist[idx].flags & IMAGEFILE) || (filelist->filelist[idx].flags & MOVIEFILE) ) {
|
||||
if ( (filelist->filelist[idx].flags & (IMAGEFILE|MOVIEFILE|BLENDERFILE)) ) {
|
||||
FileImage* limg = MEM_callocN(sizeof(struct FileImage), "loadimage");
|
||||
BLI_strncpy(limg->path, filelist->filelist[idx].path, FILE_MAX);
|
||||
limg->index= idx;
|
||||
@@ -1364,4 +1366,4 @@ void thumbnails_stop(struct FileList* filelist, const struct bContext* C)
|
||||
int thumbnails_running(struct FileList* filelist, const struct bContext* C)
|
||||
{
|
||||
return WM_jobs_test(CTX_wm_manager(C), filelist);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,13 +50,14 @@ typedef enum ThumbSize {
|
||||
|
||||
typedef enum ThumbSource {
|
||||
THB_SOURCE_IMAGE,
|
||||
THB_SOURCE_MOVIE
|
||||
THB_SOURCE_MOVIE,
|
||||
THB_SOURCE_BLEND
|
||||
} ThumbSource;
|
||||
|
||||
// IB_metadata
|
||||
|
||||
/* create thumbnail for file and returns new imbuf for thumbnail */
|
||||
ImBuf* IMB_thumb_create(const char* path, ThumbSize size, ThumbSource source);
|
||||
ImBuf* IMB_thumb_create(const char* path, ThumbSize size, ThumbSource source, ImBuf *ibuf);
|
||||
|
||||
/* read thumbnail for file and returns new imbuf for thumbnail */
|
||||
ImBuf* IMB_thumb_read(const char* path, ThumbSize size);
|
||||
@@ -70,6 +71,8 @@ ImBuf* IMB_thumb_manage(const char* path, ThumbSize size, ThumbSource source);
|
||||
/* create the necessary dirs to store the thumbnails */
|
||||
void IMB_thumb_makedirs();
|
||||
|
||||
/* special function for loading a thumbnail embedded into a blend file */
|
||||
ImBuf *IMB_loadblend_thumb(const char *path);
|
||||
|
||||
#endif /* _IMB_THUMBS_H */
|
||||
|
||||
|
||||
@@ -241,9 +241,8 @@ void IMB_thumb_makedirs()
|
||||
}
|
||||
|
||||
/* create thumbnail for file and returns new imbuf for thumbnail */
|
||||
ImBuf* IMB_thumb_create(const char* path, ThumbSize size, ThumbSource source)
|
||||
ImBuf* IMB_thumb_create(const char* path, ThumbSize size, ThumbSource source, ImBuf *img)
|
||||
{
|
||||
ImBuf *img = 0;
|
||||
char uri[URI_MAX];
|
||||
char desc[URI_MAX+22];
|
||||
char tpath[FILE_MAX];
|
||||
@@ -285,8 +284,18 @@ ImBuf* IMB_thumb_create(const char* path, ThumbSize size, ThumbSource source)
|
||||
img = IMB_allocImBuf(0,0,32, IB_rect | IB_metadata, 0);
|
||||
if (!img) return 0;
|
||||
} else {
|
||||
if (THB_SOURCE_IMAGE == source) {
|
||||
img = IMB_loadiffname(path, IB_rect | IB_metadata);
|
||||
if (THB_SOURCE_IMAGE == source || THB_SOURCE_BLEND == source) {
|
||||
|
||||
/* only load if we didnt give an image */
|
||||
if(img==NULL) {
|
||||
if(THB_SOURCE_BLEND == source) {
|
||||
img = IMB_loadblend_thumb(path);
|
||||
}
|
||||
else {
|
||||
img = IMB_loadiffname(path, IB_rect | IB_metadata);
|
||||
}
|
||||
}
|
||||
|
||||
if (img != NULL) {
|
||||
stat(path, &info);
|
||||
sprintf(mtime, "%ld", info.st_mtime);
|
||||
@@ -425,10 +434,10 @@ ImBuf* IMB_thumb_manage(const char* path, ThumbSize size, ThumbSource source)
|
||||
IMB_thumb_delete(path, THB_NORMAL);
|
||||
IMB_thumb_delete(path, THB_LARGE);
|
||||
IMB_thumb_delete(path, THB_FAIL);
|
||||
img = IMB_thumb_create(path, size, source);
|
||||
img = IMB_thumb_create(path, size, source, NULL);
|
||||
if(!img){
|
||||
/* thumb creation failed, write fail thumb */
|
||||
img = IMB_thumb_create(path, THB_FAIL, source);
|
||||
img = IMB_thumb_create(path, THB_FAIL, source, NULL);
|
||||
if (img) {
|
||||
/* we don't need failed thumb anymore */
|
||||
IMB_freeImBuf(img);
|
||||
@@ -438,10 +447,10 @@ ImBuf* IMB_thumb_manage(const char* path, ThumbSize size, ThumbSource source)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
img = IMB_thumb_create(path, size, source);
|
||||
img = IMB_thumb_create(path, size, source, NULL);
|
||||
if(!img){
|
||||
/* thumb creation failed, write fail thumb */
|
||||
img = IMB_thumb_create(path, THB_FAIL, source);
|
||||
img = IMB_thumb_create(path, THB_FAIL, source, NULL);
|
||||
if (img) {
|
||||
/* we don't need failed thumb anymore */
|
||||
IMB_freeImBuf(img);
|
||||
|
||||
122
source/blender/imbuf/intern/thumbs_blend.c
Normal file
122
source/blender/imbuf/intern/thumbs_blend.c
Normal file
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* $Id:
|
||||
*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Contributor(s): Campbell Barton.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "zlib.h"
|
||||
|
||||
#include "BKE_utildefines.h"
|
||||
#include "BKE_global.h"
|
||||
|
||||
#include "IMB_imbuf_types.h"
|
||||
#include "IMB_imbuf.h"
|
||||
#include "IMB_thumbs.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
ImBuf *IMB_loadblend_thumb(const char *path)
|
||||
{
|
||||
char buf[8];
|
||||
int code= 0;
|
||||
char endian, pointer_size;
|
||||
char endian_switch;
|
||||
int len, im_len, x, y;
|
||||
int *rect= NULL;
|
||||
|
||||
gzFile gzfile;
|
||||
|
||||
ImBuf *img;
|
||||
|
||||
/* not necessarily a gzip */
|
||||
gzfile = gzopen(path, "rb");
|
||||
|
||||
if (NULL == gzfile ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* read the blend file header */
|
||||
if(gzread(gzfile, buf, 8) < 8) goto thumb_error;
|
||||
if(strncmp(buf, "BLENDER", 7)) goto thumb_error;
|
||||
|
||||
if(buf[7]=='-') pointer_size= 8;
|
||||
else if(buf[7]=='_') pointer_size= 4;
|
||||
else goto thumb_error;
|
||||
|
||||
/* read the next 4 bytes, only need the first char, ignore the version */
|
||||
/* endian and vertsion (ignored) */
|
||||
if(gzread(gzfile, buf, 4) < 4) goto thumb_error;
|
||||
|
||||
if(buf[0]=='V') endian= B_ENDIAN; /* big: PPC */
|
||||
else if(buf[0]=='v') endian= L_ENDIAN; /* little: x86 */
|
||||
else goto thumb_error;
|
||||
|
||||
while(gzread(gzfile, &code, 4) == 4) {
|
||||
endian_switch = ((ENDIAN_ORDER != endian)) ? 1 : 0;
|
||||
|
||||
if(gzread(gzfile, buf, 4) < 4) goto thumb_error;
|
||||
len = *( (int *)((void *)buf) );
|
||||
if(endian_switch) SWITCH_INT(len);
|
||||
|
||||
/* finally read the rest of the bhead struct, pointer and 2 ints */
|
||||
if(gzread(gzfile, buf, pointer_size) < pointer_size) goto thumb_error;
|
||||
if(gzread(gzfile, buf, 8) < 8) goto thumb_error;
|
||||
/* we dont actually care whats in the bhead */
|
||||
|
||||
if (code==REND) {
|
||||
gzseek(gzfile, len, SEEK_CUR); /* skip to the next */
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* using 'TEST' since new names segfault when loading in old blenders */
|
||||
if(code != TEST) goto thumb_error;
|
||||
|
||||
/* finally malloc and read the data */
|
||||
rect= MEM_mallocN(len, "imb_loadblend_thumb");
|
||||
|
||||
if(gzread(gzfile, rect, len) < len) goto thumb_error;
|
||||
|
||||
/* read ok! */
|
||||
gzclose(gzfile);
|
||||
|
||||
x= rect[0]; y= rect[1];
|
||||
if(endian_switch) { SWITCH_INT(x); SWITCH_INT(y); }
|
||||
|
||||
im_len = x * y * sizeof(int);
|
||||
|
||||
img = IMB_allocImBuf(x, y, 32, IB_rect | IB_metadata, 0);
|
||||
|
||||
memcpy(img->rect, rect + 2, im_len);
|
||||
|
||||
MEM_freeN(rect);
|
||||
|
||||
return img;
|
||||
|
||||
thumb_error:
|
||||
gzclose(gzfile);
|
||||
if(rect) MEM_freeN(rect);
|
||||
return NULL;
|
||||
}
|
||||
@@ -77,10 +77,15 @@
|
||||
|
||||
#include "RNA_access.h"
|
||||
|
||||
#include "IMB_imbuf.h"
|
||||
#include "IMB_imbuf_types.h"
|
||||
#include "IMB_thumbs.h"
|
||||
|
||||
#include "ED_datafiles.h"
|
||||
#include "ED_object.h"
|
||||
#include "ED_screen.h"
|
||||
#include "ED_sculpt.h"
|
||||
#include "ED_view3d.h"
|
||||
#include "ED_util.h"
|
||||
|
||||
#include "GHOST_C-api.h"
|
||||
@@ -486,12 +491,53 @@ static void do_history(char *name, ReportList *reports)
|
||||
BKE_report(reports, RPT_ERROR, "Unable to make version backup");
|
||||
}
|
||||
|
||||
/* writes a thumbnail for a blendfile */
|
||||
static void writeThumb(const char *path, Scene *scene, int **thumb_pt)
|
||||
{
|
||||
/* will be scaled down, but gives some nice oversampling */
|
||||
ImBuf *ibuf;
|
||||
int *thumb;
|
||||
|
||||
*thumb_pt= NULL;
|
||||
|
||||
if(G.background || scene->camera==NULL)
|
||||
return;
|
||||
|
||||
thumb = MEM_mallocN(((2 + (BLEN_THUMB_SIZE * BLEN_THUMB_SIZE))) * sizeof(int), "write_file thumb");
|
||||
|
||||
/* gets scaled to BLEN_THUMB_SIZE */
|
||||
ibuf= ED_view3d_draw_offscreen_imbuf_simple(scene, BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2, OB_SOLID);
|
||||
|
||||
if(ibuf) {
|
||||
|
||||
/* dirty oversampling */
|
||||
IMB_scaleImBuf(ibuf, BLEN_THUMB_SIZE, BLEN_THUMB_SIZE);
|
||||
|
||||
/* first write into thumb buffer */
|
||||
thumb[0] = BLEN_THUMB_SIZE;
|
||||
thumb[1] = BLEN_THUMB_SIZE;
|
||||
memcpy(thumb + 2, ibuf->rect, BLEN_THUMB_SIZE * BLEN_THUMB_SIZE * sizeof(int));
|
||||
|
||||
/* the image is scaled here */
|
||||
ibuf= IMB_thumb_create(path, THB_NORMAL, THB_SOURCE_BLEND, ibuf);
|
||||
}
|
||||
|
||||
if (ibuf) {
|
||||
IMB_freeImBuf(ibuf);
|
||||
}
|
||||
|
||||
/* must be freed by caller */
|
||||
*thumb_pt= thumb;
|
||||
}
|
||||
|
||||
int WM_write_file(bContext *C, char *target, int fileflags, ReportList *reports)
|
||||
{
|
||||
Library *li;
|
||||
int len;
|
||||
char di[FILE_MAX];
|
||||
|
||||
|
||||
int *thumb= NULL;
|
||||
|
||||
len = strlen(target);
|
||||
|
||||
if (len == 0) {
|
||||
@@ -532,7 +578,10 @@ int WM_write_file(bContext *C, char *target, int fileflags, ReportList *reports)
|
||||
|
||||
do_history(di, reports);
|
||||
|
||||
if (BLO_write_file(CTX_data_main(C), di, fileflags, reports)) {
|
||||
/* blend file thumbnail */
|
||||
writeThumb(di, CTX_data_scene(C), &thumb);
|
||||
|
||||
if (BLO_write_file(CTX_data_main(C), di, fileflags, reports, thumb)) {
|
||||
strcpy(G.sce, di);
|
||||
G.relbase_valid = 1;
|
||||
strcpy(G.main->name, di); /* is guaranteed current file */
|
||||
@@ -546,7 +595,11 @@ int WM_write_file(bContext *C, char *target, int fileflags, ReportList *reports)
|
||||
else G.fileflags &= ~G_FILE_AUTOPLAY;
|
||||
|
||||
writeBlog();
|
||||
} else {
|
||||
|
||||
if(thumb) MEM_freeN(thumb);
|
||||
}
|
||||
else {
|
||||
if(thumb) MEM_freeN(thumb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -571,7 +624,7 @@ int WM_write_homefile(bContext *C, wmOperator *op)
|
||||
/* force save as regular blend file */
|
||||
fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_LOCK | G_FILE_SIGN);
|
||||
|
||||
BLO_write_file(CTX_data_main(C), tstr, fileflags, op->reports);
|
||||
BLO_write_file(CTX_data_main(C), tstr, fileflags, op->reports, NULL);
|
||||
|
||||
G.save_over= 0;
|
||||
|
||||
@@ -640,7 +693,7 @@ void wm_autosave_timer(const bContext *C, wmWindowManager *wm, wmTimer *wt)
|
||||
fileflags = G.fileflags & ~(G_FILE_COMPRESS|G_FILE_AUTOPLAY |G_FILE_LOCK|G_FILE_SIGN);
|
||||
|
||||
/* no error reporting to console */
|
||||
BLO_write_file(CTX_data_main(C), filename, fileflags, NULL);
|
||||
BLO_write_file(CTX_data_main(C), filename, fileflags, NULL, NULL);
|
||||
|
||||
/* do timer after file write, just in case file write takes a long time */
|
||||
wm->autosavetimer= WM_event_add_timer(wm, NULL, TIMERAUTOSAVE, U.savetime*60.0);
|
||||
|
||||
Reference in New Issue
Block a user