Initial attempt at i18n support using gettext
[synfig.git] / synfig-studio / trunk / src / gtkmm / asyncrenderer.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file asyncrenderer.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 "asyncrenderer.h"
33 #include "app.h"
34 #include <glibmm/thread.h>
35 #include <glibmm/dispatcher.h>
36
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40
41 #ifdef HAVE_SYS_TYPES_H
42 #include <sys/types.h>
43 #endif
44
45 #ifdef HAVE_SYS_WAIT_H
46 #include <sys/wait.h>
47 #endif
48
49 #ifdef HAVE_SIGNAL_H
50 #include <signal.h>
51 #endif
52
53 #include <synfig/general.h>
54 #include <ETL/clock>
55
56 #include "general.h"
57
58 #endif
59
60 /* === U S I N G =========================================================== */
61
62 using namespace std;
63 using namespace etl;
64 using namespace synfig;
65 using namespace studio;
66
67 #define BOREDOM_TIMEOUT         50
68
69 #define REJOIN_ON_STOP  1
70
71 // The Glib::Dispatcher class is broken as of Glibmm 2.4.5.
72 // Defining this macro enables the workaround.
73 #define GLIB_DISPATCHER_BROKEN 1
74
75 /* === C L A S S E S ======================================================= */
76
77 class AsyncTarget_Tile : public synfig::Target_Tile
78 {
79 public:
80         etl::handle<synfig::Target_Tile> warm_target;
81
82         struct tile_t
83         {
84                 Surface surface;
85                 int x,y;
86                 tile_t(const Surface& surface,int x, int y):
87                         surface(surface),
88                         x(x),y(y)
89                 {
90                 }
91         };
92         std::list<tile_t> tile_queue;
93         Glib::Mutex mutex;
94
95 #ifndef GLIB_DISPATCHER_BROKEN
96         Glib::Dispatcher tile_ready_signal;
97 #endif
98         Glib::Cond cond_tile_queue_empty;
99         bool alive_flag;
100
101         sigc::connection ready_connection;
102
103 public:
104         AsyncTarget_Tile(etl::handle<synfig::Target_Tile> warm_target):
105                 warm_target(warm_target)
106         {
107                 set_avoid_time_sync(warm_target->get_avoid_time_sync());
108                 set_tile_w(warm_target->get_tile_w());
109                 set_tile_h(warm_target->get_tile_h());
110                 set_canvas(warm_target->get_canvas());
111                 set_quality(warm_target->get_quality());
112                 set_remove_alpha(warm_target->get_remove_alpha());
113                 set_threads(warm_target->get_threads());
114                 set_clipping(warm_target->get_clipping());
115                 set_rend_desc(&warm_target->rend_desc());
116                 alive_flag=true;
117 #ifndef GLIB_DISPATCHER_BROKEN
118                 ready_connection=tile_ready_signal.connect(sigc::mem_fun(*this,&AsyncTarget_Tile::tile_ready));
119 #endif
120         }
121
122         ~AsyncTarget_Tile()
123         {
124                 ready_connection.disconnect();
125         }
126         void set_dead()
127         {
128                 Glib::Mutex::Lock lock(mutex);
129                 alive_flag=false;
130         }
131
132         virtual int total_tiles()const
133         {
134                 return warm_target->total_tiles();
135         }
136
137         virtual int next_tile(int& x, int& y)
138         {
139                 if(!alive_flag)
140                         return 0;
141
142                 return warm_target->next_tile(x,y);
143         }
144
145         virtual int next_frame(Time& time)
146         {
147                 if(!alive_flag)
148                         return 0;
149                 return warm_target->next_frame(time);
150         }
151
152         virtual bool start_frame(synfig::ProgressCallback *cb=0)
153         {
154                 if(!alive_flag)
155                         return false;
156                 return warm_target->start_frame(cb);
157         }
158
159         virtual bool add_tile(const synfig::Surface &surface, int gx, int gy)
160         {
161                 assert(surface);
162                 if(!alive_flag)
163                         return false;
164                 Glib::Mutex::Lock lock(mutex);
165                 tile_queue.push_back(tile_t(surface,gx,gy));
166                 if(tile_queue.size()==1)
167                 {
168 #ifdef GLIB_DISPATCHER_BROKEN
169                 ready_connection=Glib::signal_timeout().connect(
170                         sigc::bind_return(
171                                 sigc::mem_fun(*this,&AsyncTarget_Tile::tile_ready),
172                                 false
173                         )
174                         ,0
175                 );
176 #else
177                 tile_ready_signal();
178 #endif
179                 }
180
181                 return alive_flag;
182         }
183
184         void tile_ready()
185         {
186                 Glib::Mutex::Lock lock(mutex);
187                 if(!alive_flag)
188                 {
189                         tile_queue.clear();
190                         cond_tile_queue_empty.signal();
191                         return;
192                 }
193                 while(!tile_queue.empty() && alive_flag)
194                 {
195                         tile_t& tile(tile_queue.front());
196
197                         alive_flag=warm_target->add_tile(tile.surface,tile.x,tile.y);
198
199                         tile_queue.pop_front();
200                 }
201                 cond_tile_queue_empty.signal();
202         }
203
204         virtual void end_frame()
205         {
206                 if (!single_threaded())
207                 {
208                         while(alive_flag)
209                         {
210                                 Glib::Mutex::Lock lock(mutex);
211                                 if(!tile_queue.empty() && alive_flag)
212                                 {
213                                         if(cond_tile_queue_empty.timed_wait(mutex,Glib::TimeVal(0,BOREDOM_TIMEOUT)))
214                                                 break;
215                                 }
216                                 else
217                                         break;
218                         }
219                 }
220                 Glib::Mutex::Lock lock(mutex);
221                 if(!alive_flag)
222                         return;
223                 return warm_target->end_frame();
224         }
225 };
226
227
228
229 class AsyncTarget_Scanline : public synfig::Target_Scanline
230 {
231 public:
232         etl::handle<synfig::Target_Scanline> warm_target;
233
234         int scanline_;
235         Surface surface;
236
237         Glib::Mutex mutex;
238
239 #ifndef GLIB_DISPATCHER_BROKEN
240         Glib::Dispatcher frame_ready_signal;
241 #endif
242         Glib::Cond cond_frame_queue_empty;
243         bool alive_flag;
244         bool ready_next;
245         sigc::connection ready_connection;
246
247
248 public:
249         AsyncTarget_Scanline(etl::handle<synfig::Target_Scanline> warm_target):
250                 warm_target(warm_target)
251         {
252                 set_avoid_time_sync(warm_target->get_avoid_time_sync());
253                 set_canvas(warm_target->get_canvas());
254                 set_quality(warm_target->get_quality());
255                 set_remove_alpha(warm_target->get_remove_alpha());
256                 set_threads(warm_target->get_threads());
257                 set_rend_desc(&warm_target->rend_desc());
258                 alive_flag=true;
259 #ifndef GLIB_DISPATCHER_BROKEN
260                 ready_connection=frame_ready_signal.connect(sigc::mem_fun(*this,&AsyncTarget_Scanline::frame_ready));
261 #endif
262                 surface.set_wh(warm_target->rend_desc().get_w(),warm_target->rend_desc().get_h());
263         }
264
265         ~AsyncTarget_Scanline()
266         {
267                 ready_connection.disconnect();
268         }
269
270         virtual int next_frame(Time& time)
271         {
272                 if(!alive_flag)
273                         return 0;
274                 return warm_target->next_frame(time);
275
276         }
277
278         void set_dead()
279         {
280                 Glib::Mutex::Lock lock(mutex);
281                 alive_flag=false;
282         }
283
284         virtual bool start_frame(synfig::ProgressCallback */*cb*/=0)
285         {
286                 return alive_flag;
287         }
288
289         virtual void end_frame()
290         {
291                 {
292                         Glib::Mutex::Lock lock(mutex);
293
294                         if(!alive_flag)
295                                 return;
296                         ready_next=false;
297
298 #ifdef GLIB_DISPATCHER_BROKEN
299                 ready_connection=Glib::signal_timeout().connect(
300                         sigc::bind_return(
301                                 sigc::mem_fun(*this,&AsyncTarget_Scanline::frame_ready),
302                                 false
303                         )
304                         ,0
305                 );
306 #else
307                         frame_ready_signal();
308 #endif
309                 }
310
311                 if (single_threaded())
312                         signal_progress()();
313                 else
314                         while(alive_flag && !ready_next)
315                         {
316                                 Glib::Mutex::Lock lock(mutex);
317                                 if(cond_frame_queue_empty.timed_wait(mutex,Glib::TimeVal(0,BOREDOM_TIMEOUT)))
318                                         break;
319                         }
320         }
321
322
323         virtual Color * start_scanline(int scanline)
324         {
325                 Glib::Mutex::Lock lock(mutex);
326
327                 return surface[scanline];
328         }
329
330         virtual bool end_scanline()
331         {
332                 return alive_flag;
333         }
334
335         void frame_ready()
336         {
337                 Glib::Mutex::Lock lock(mutex);
338                 if(alive_flag)
339                         alive_flag=warm_target->add_frame(&surface);
340                 if (!single_threaded()) cond_frame_queue_empty.signal();
341                 ready_next=true;
342         }
343 };
344
345 /* === G L O B A L S ======================================================= */
346
347 /* === P R O C E D U R E S ================================================= */
348
349 /* === M E T H O D S ======================================================= */
350
351 AsyncRenderer::AsyncRenderer(etl::handle<synfig::Target> target_,synfig::ProgressCallback *cb):
352         error(false),
353         success(false),
354         cb(cb),
355         updating(false)
356 {
357         render_thread=0;
358         if(etl::handle<synfig::Target_Tile>::cast_dynamic(target_))
359         {
360                 etl::handle<AsyncTarget_Tile> wrap_target(
361                         new AsyncTarget_Tile(etl::handle<synfig::Target_Tile>::cast_dynamic(target_))
362                 );
363
364                 signal_stop_.connect(sigc::mem_fun(*wrap_target,&AsyncTarget_Tile::set_dead));
365
366                 target=wrap_target;
367         }
368         else if(etl::handle<synfig::Target_Scanline>::cast_dynamic(target_))
369         {
370                 etl::handle<AsyncTarget_Scanline> wrap_target(
371                         new AsyncTarget_Scanline(
372                                 etl::handle<synfig::Target_Scanline>::cast_dynamic(target_)
373                         )
374                 );
375
376                 signal_stop_.connect(sigc::mem_fun(*wrap_target,&AsyncTarget_Scanline::set_dead));
377
378                 target=wrap_target;
379         }
380 }
381
382 AsyncRenderer::~AsyncRenderer()
383 {
384         stop();
385 }
386
387 void
388 AsyncRenderer::stop()
389 {
390         if(target)
391         {
392                 Glib::Mutex::Lock lock(mutex);
393                 done_connection.disconnect();
394
395                 if(render_thread)
396                 {
397                         signal_stop_();
398
399 #if REJOIN_ON_STOP
400                         if (!single_threaded()) render_thread->join();
401 #endif
402
403                         // Make sure all the dispatch crap is cleared out
404                         //Glib::MainContext::get_default()->iteration(false);
405
406                         if(success)
407                                 signal_success_();
408
409                         signal_finished_();
410
411                         target=0;
412                         render_thread=0;
413                 }
414         }
415 }
416
417 void
418 AsyncRenderer::pause()
419 {
420 }
421
422 void
423 AsyncRenderer::resume()
424 {
425 }
426
427 void
428 AsyncRenderer::start()
429 {
430         done_connection=Glib::signal_timeout().connect(
431                 sigc::bind_return(
432                         mem_fun(*this,&AsyncRenderer::start_),
433                         false
434                 )
435                 ,50
436         );
437 }
438
439 void
440 AsyncRenderer::rendering_progress()
441 {
442         updating = true;
443         while(studio::App::events_pending()) studio::App::iteration(false);
444         updating = false;
445 }
446
447 void
448 AsyncRenderer::start_()
449 {
450         error=false;success=false;
451         if(target)
452         {
453 #ifndef GLIB_DISPATCHER_BROKEN
454                 done_connection=signal_done_.connect(mem_fun(*this,&AsyncRenderer::stop));
455 #endif
456
457                 if (single_threaded())
458                 {
459                         synfig::info("%s:%d rendering in the same thread", __FILE__, __LINE__);
460                         target->signal_progress().connect(sigc::mem_fun(this,&AsyncRenderer::rendering_progress));
461                         render_thread = (Glib::Thread*)1;
462                         render_target();
463                 }
464                 else
465                 {
466                         render_thread=Glib::Thread::create(
467                                 sigc::mem_fun(*this,&AsyncRenderer::render_target),
468 #if REJOIN_ON_STOP
469                                 true
470 #else
471                                 false
472 #endif
473                                 );
474                         assert(render_thread);
475                 }
476         }
477         else
478         {
479                 stop();
480         }
481 }
482
483 void
484 AsyncRenderer::render_target()
485 {
486         etl::handle<Target> target(AsyncRenderer::target);
487
488         if(target && target->render())
489         {
490                 success=true;
491         }
492         else
493         {
494                 error=true;
495 #ifndef REJOIN_ON_STOP
496                 return;
497 #endif
498         }
499
500         if(mutex.trylock())
501         {
502 #ifdef GLIB_DISPATCHER_BROKEN
503                 done_connection=Glib::signal_timeout().connect(
504                         sigc::bind_return(
505                                 mem_fun(*this,&AsyncRenderer::stop),
506                                 false
507                         )
508                         ,0
509                 );
510 #else
511                 signal_done_.emit();
512 #endif
513                 mutex.unlock();
514         }
515 }