Fix a crash when running single-threaded and dragging the time slider.
[synfig.git] / synfig-studio / trunk / src / gtkmm / autorecover.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file autorecover.cpp
3 **      \brief Template File
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **
10 **      This package is free software; you can redistribute it and/or
11 **      modify it under the terms of the GNU General Public License as
12 **      published by the Free Software Foundation; either version 2 of
13 **      the License, or (at your option) any later version.
14 **
15 **      This package is distributed in the hope that it will be useful,
16 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 **      General Public License for more details.
19 **      \endlegal
20 */
21 /* ========================================================================= */
22
23 /* === H E A D E R S ======================================================= */
24
25 #ifdef USING_PCH
26 #       include "pch.h"
27 #else
28 #ifdef HAVE_CONFIG_H
29 #       include <config.h>
30 #endif
31
32 #include "autorecover.h"
33
34 #ifdef HAVE_SYS_ERRNO_H
35 #include <sys/errno.h>
36 #endif
37 //#include <unistd.h>
38 #include "app.h"
39 #include <synfig/savecanvas.h>
40 #include <synfig/loadcanvas.h>
41 #include <fstream>
42 #include <iostream>
43 #include "instance.h"
44
45 #ifdef HAVE_SYS_STAT_H
46 #include <sys/stat.h>
47 #endif
48
49 #ifdef HAVE_SYS_TIME_H
50 #include <sys/time.h>
51 #endif
52
53 #ifdef HAVE_SYS_WAIT_H
54 #include <sys/wait.h>
55 #endif
56
57 #ifdef HAVE_SYS_RESOURCE_H
58 #include <sys/resource.h>
59 #endif
60
61 #ifdef HAVE_UNISTD_H
62 #include <unistd.h>
63 #endif
64
65 #endif
66
67 /* === U S I N G =========================================================== */
68
69 using namespace std;
70 using namespace etl;
71 using namespace synfig;
72 using namespace studio;
73
74 /* === M A C R O S ========================================================= */
75
76 #ifdef _WIN32
77 #define mkdir(x,y) mkdir(x)
78 #endif
79
80 /* === G L O B A L S ======================================================= */
81
82 /* === P R O C E D U R E S ================================================= */
83
84 /* === M E T H O D S ======================================================= */
85
86 AutoRecover::AutoRecover()
87 {
88         // Three Minutes
89         set_timeout(3*60*1000);
90
91         if(mkdir(get_shadow_directory().c_str(),ACCESSPERMS)<0)
92         {
93                 if(errno!=EEXIST)
94                         synfig::error("UNABLE TO CREATE \"%s\"",get_shadow_directory().c_str());
95         }
96         else
97         {
98                 synfig::info("Created directory \"%s\"",get_shadow_directory().c_str());
99         }
100 }
101
102 AutoRecover::~AutoRecover()
103 {
104 }
105
106 synfig::String
107 AutoRecover::get_shadow_directory()
108 {
109         return Glib::build_filename(App::get_user_app_directory(),"tmp");
110 }
111
112 int
113 AutoRecover::pid()
114 {
115 //      return getpid();
116         return 0;
117 }
118
119 void
120 AutoRecover::set_timeout(int milliseconds)
121 {
122         timeout=milliseconds;
123         auto_backup_connect.disconnect();
124         if(timeout)
125                 auto_backup_connect=Glib::signal_timeout().connect(sigc::ptr_fun(&AutoRecover::auto_backup),timeout);
126 //              auto_backup_connect=App::main.get_context()->signal_timeout().connect(sigc::mem_fun(&AutoRecover::auto_backup),timeout);
127 }
128
129 synfig::String
130 AutoRecover::get_shadow_file_name(const synfig::String& filename)
131 {
132         unsigned int hash1(0xdeadbeef);
133         unsigned int hash2(0x83502529);
134         char* str_hash1(reinterpret_cast<char*>(&hash1));
135         char* str_hash2(reinterpret_cast<char*>(&hash2));
136
137         // First we need to hash up the directory
138         {
139                 String pool(dirname(filename));
140
141                 while(pool.size()>4)
142                 {
143                         str_hash1[0]^=pool[1];str_hash1[1]^=pool[2];str_hash1[2]^=pool[3];str_hash1[3]^=pool[0];
144                         str_hash2[3]+=pool[0];str_hash2[2]+=pool[1];str_hash2[1]+=pool[2];str_hash2[0]+=pool[3];
145                         swap(hash1,hash2);
146                         pool=String(pool,4,pool.size());
147                 }
148                 while(pool.size())
149                 {
150                         str_hash1[0]^=pool[0];
151                         str_hash1[2]^=pool[0];
152                         str_hash2[1]^=pool[0];
153                         str_hash2[3]^=pool[0];
154                         swap(hash1,hash2);
155                         pool=String(pool,1,pool.size());
156                 }
157         }
158         hash1^=hash2;
159
160         return Glib::build_filename(get_shadow_directory(),strprintf("%08X-%s",hash1,basename(filename).c_str()));
161
162 //      return dirname(filename) + ETL_DIRECTORY_SEPARATOR + ".shadow_" + basename(filename);
163 }
164
165 bool
166 AutoRecover::cleanup_pid(int pid)
167 {
168 #ifdef HAVE_FORK
169         int status=0;
170         if(waitpid(pid,&status,WNOHANG)==-1)
171         {
172                 synfig::info("PID %d isn't a zombie yet",pid);
173                 return true;
174         }
175         if(WEXITSTATUS(status)!=0)
176         {
177                 synfig::error("Autobackup seems to have failed! (PID=%d)",pid);
178         }
179         else
180                 synfig::info("PID=%d has been cleaned up",pid);
181 #endif
182         return false;
183 }
184
185 bool
186 AutoRecover::auto_backup()
187 {
188         int pid(0);
189
190 #ifdef HAVE_FORK
191         pid=fork();
192 #endif
193
194         if(pid<=0)
195         {
196 #ifdef HAVE_SETPRIORITY
197                 // make us low priority so that we don't
198                 // cause the machine to slow down too much
199                 setpriority(PRIO_PROCESS,0,15);
200 #endif
201
202                 try
203                 {
204                         std::list<etl::handle<Instance> >::iterator iter;
205
206                         std::string filename=App::get_config_file("autorecovery");
207                         std::ofstream file(filename.c_str());
208
209                         int savecount(0);
210
211                         for(iter=App::instance_list.begin();iter!=App::instance_list.end();++iter)
212                         {
213                                 // If this file hasn't even been changed
214                                 // since it was last saved, then don't bother
215                                 // backing it up.
216                                 if((*iter)->get_action_count()==0)
217                                         continue;
218
219                                 Canvas::Handle canvas((*iter)->get_canvas());
220                                 file<<canvas->get_file_name()<<endl;
221                                 save_canvas(get_shadow_file_name(canvas->get_file_name()),canvas);
222                                 savecount++;
223                         }
224
225                         if(savecount)
226                                 synfig::info("AutoRecover::auto_backup(): %d Files backed up.",savecount);
227                 }
228                 catch(...)
229                 {
230                         synfig::error("AutoRecover::auto_backup(): UNKNOWN EXCEPTION THROWN.");
231                         synfig::error("AutoRecover::auto_backup(): FILES NOT BACKED UP.");
232                 }
233
234 #ifdef HAVE_FORK
235                 if(pid==0)
236                 {
237                         _exit(0);
238                 }
239 #endif
240         }
241
242 #ifdef HAVE_FORK
243         Glib::signal_timeout().connect(
244                 sigc::bind(
245                         sigc::ptr_fun(&AutoRecover::cleanup_pid),
246                         pid
247                 ),
248                 60*1000
249         );
250 #endif
251
252         // Also go ahead and save the settings
253         App::save_settings();
254
255         return true;
256 }
257
258 bool
259 AutoRecover::recovery_needed()const
260 {
261         std::string filename=App::get_config_file("autorecovery");
262         std::ifstream file(filename.c_str());
263         if(!file)
264                 return false;
265
266         while(file)
267         {
268                 std::string filename;
269                 getline(file,filename);
270                 if(!filename.empty())
271                         return true;
272         }
273
274         return false;
275 }
276
277 bool
278 AutoRecover::recover()
279 {
280         std::string filename=App::get_config_file("autorecovery");
281         std::ifstream file(filename.c_str());
282         if(!file)
283                 return false;
284         bool success=true;
285
286         while(file)
287         {
288                 std::string filename;
289                 getline(file,filename);
290                 if(filename.empty())
291                         continue;
292
293                 // Open the file
294                 if(App::open_as(get_shadow_file_name(filename),filename))
295                 {
296                         // Correct the file name
297                         App::instance_list.back()->set_file_name(filename);
298
299                         // This file isn't saved! mark it as such
300                         App::instance_list.back()->inc_action_count();
301                 }
302                 else
303                         success=false;
304         }
305
306         return success;
307 }
308
309 void
310 AutoRecover::normal_shutdown()
311 {
312         // Turn off the timer
313         auto_backup_connect.disconnect();
314
315         std::string filename=App::get_config_file("autorecovery");
316         remove(filename.c_str());
317 }
318
319 void
320 AutoRecover::clear_backup(synfig::Canvas::Handle canvas)
321 {
322         if(canvas)
323                 remove(get_shadow_file_name(canvas->get_file_name()).c_str());
324 }