upgrade bccmdl.py to python3, patch from FeRD (Frank Dana)
[goodguy/cinelerra.git] / cinelerra-5.1 / guicast / vicon.C
1 #include "vicon.h"
2
3 #include "bctimer.h"
4 #include "bcwindow.h"
5 #include "bccolors.h"
6 #include "keys.h"
7 #include "mutex.h"
8 #include "condition.h"
9
10 VIcon::
11 VIcon(int vw, int vh, double rate)
12 {
13         this->vw = vw;
14         this->vh = vh;
15         this->frame_rate = rate;
16
17         cycle_start = 0;
18         age = 0;
19         seq_no = 0;
20         in_use = 1;
21         hidden = 0;
22         audio_data = 0;
23         audio_size = 0;
24         playing_audio = 0;
25 }
26
27 VIcon::
28 ~VIcon()
29 {
30         clear_images();
31         delete [] audio_data;
32 }
33
34 void VIcon::
35 add_image(VFrame *frm, int ww, int hh, int vcmdl)
36 {
37         VIFrame *vifrm = new VIFrame(ww, hh, vcmdl);
38         VFrame *img = *vifrm;
39         img->transfer_from(frm);
40         images.append(vifrm);
41 }
42
43 void VIcon::
44 draw_vframe(VIconThread *vt, BC_WindowBase *wdw, int x, int y)
45 {
46         VFrame *vfrm = frame();
47         if( !vfrm ) return;
48         int sx0 = 0, sx1 = sx0 + vt->view_w;
49         int sy0 = 0, sy1 = sy0 + vt->view_h;
50         int dx0 = x, dx1 = dx0 + vw;
51         int dy0 = y, dy1 = dy0 + vh;
52         if( (x=vt->draw_x0-dx0) > 0 ) { sx0 += (x*vt->view_w)/vw;  dx0 = vt->draw_x0; }
53         if( (x=dx1-vt->draw_x1) > 0 ) { sx1 -= (x*vt->view_w)/vw;  dx1 = vt->draw_x1; }
54         if( (y=vt->draw_y0-dy0) > 0 ) { sy0 += (y*vt->view_h)/vh;  dy0 = vt->draw_y0; }
55         if( (y=dy1-vt->draw_y1) > 0 ) { sy1 -= (y*vt->view_h)/vh;  dy1 = vt->draw_y1; }
56         int sw = sx1 - sx0, sh = sy1 - sy0;
57         int dw = dx1 - dx0, dh = dy1 - dy0;
58         if( dw > 0 && dh > 0 && sw > 0 && sh > 0 )
59                 wdw->draw_vframe(vfrm, dx0,dy0, dw,dh, sx0,sy0, sw,sh);
60 }
61
62 void VIconThread::
63 set_drawing_area(int x0, int y0, int x1, int y1)
64 {
65         draw_x0 = x0;  draw_y0 = y0;
66         draw_x1 = x1;  draw_y1 = y1;
67 }
68
69 VIcon *VIconThread::low_vicon()
70 {
71         if( !t_heap.size() ) return 0;
72         VIcon *vip = t_heap[0];
73         remove_vicon(0);
74         return vip;
75 }
76
77 void VIconThread::remove_vicon(int i)
78 {
79         int sz = t_heap.size();
80         for( int k; (k=2*(i+1)) < sz; i=k ) {
81                 if( t_heap[k]->age > t_heap[k-1]->age ) --k;
82                 t_heap[i] = t_heap[k];
83         }
84         VIcon *last = t_heap[--sz];
85         t_heap.remove_number(sz);
86         double age = last->age;
87         for( int k; i>0 && age<t_heap[k=(i-1)/2]->age; i=k )
88                 t_heap[i] = t_heap[k];
89         t_heap[i] = last;
90 }
91
92
93 VIconThread::
94 VIconThread(BC_WindowBase *wdw, int vw, int vh)
95  : Thread(1, 0, 0)
96 {
97         this->wdw = wdw;
98         this->view_win = 0;  this->vicon = 0;
99         this->view_w = vw;   this->view_h = vh;
100         this->viewing = 0;
101         this->draw_x0 = 0;   this->draw_x1 = wdw->get_w();
102         this->draw_y0 = 0;   this->draw_y1 = wdw->get_h();
103         draw_lock = new Condition(0, "VIconThread::draw_lock", 1);
104         timer = new Timer();
105         this->refresh_rate = VICON_RATE;
106         done = 0;
107         interrupted = -1;
108         stop_age = 0;
109 }
110
111 VIconThread::
112 ~VIconThread()
113 {
114         stop_drawing();
115         done = 1;
116         draw_lock->unlock();
117         if( Thread::running() ) {
118                 Thread::cancel();
119         }
120         Thread::join();
121         t_heap.remove_all_objects();
122         delete timer;
123         delete draw_lock;
124 }
125
126 void VIconThread::
127 start_drawing()
128 {
129         wdw->lock_window("VIconThread::start_drawing");
130         if( view_win )
131                 wdw->set_active_subwindow(view_win);
132         if( interrupted < 0 )
133                 draw_lock->unlock();
134         timer->update();
135         timer->subtract(-stop_age);
136         interrupted = 0;
137         wdw->unlock_window();
138 }
139
140 void VIconThread::
141 stop_drawing()
142 {
143         wdw->lock_window("VIconThread::stop_drawing");
144         close_view_popup();
145         if( !interrupted )
146                 interrupted = 1;
147         stop_age = timer->get_difference();
148         wdw->unlock_window();
149 }
150
151 int VIconThread::keypress_event(int key)
152 {
153         if( key != ESC ) return 0;
154         close_view_popup();
155         return 1;
156 }
157
158 bool VIconThread::
159 visible(VIcon *vicon, int x, int y)
160 {
161         if( vicon->hidden ) return false;
162         if( y+vicon->vh <= draw_y0 ) return false;
163         if( y >= draw_y1 ) return false;
164         if( x+vicon->vw <= draw_x0 ) return false;
165         if( x >= draw_x1 ) return false;
166         return true;
167 }
168
169 int ViewPopup::keypress_event()
170 {
171         int key = get_keypress();
172         return vt->keypress_event(key);
173 }
174
175 int ViewPopup::button_press_event()
176 {
177         return vt->popup_button_press(get_cursor_x(), get_cursor_y());
178 }
179 int ViewPopup::button_release_event()
180 {
181         return vt->popup_button_release(get_cursor_x(), get_cursor_y());
182 }
183 int ViewPopup::cursor_motion_event()
184 {
185         return vt->popup_cursor_motion(get_cursor_x(), get_cursor_y());
186 }
187
188
189 ViewPopup::ViewPopup(VIconThread *vt, VFrame *frame, int x, int y, int w, int h)
190  : BC_Popup(vt->wdw, x, y, w, h, BLACK)
191 {
192         this->vt = vt;
193 }
194
195 ViewPopup::~ViewPopup()
196 {
197         vt->wdw->set_active_subwindow(0);
198 }
199
200 ViewPopup *VIconThread::new_view_window(VFrame *frame)
201 {
202         BC_WindowBase *parent = wdw->get_parent();
203         XineramaScreenInfo *info = parent->get_xinerama_info(-1);
204         int cx = info ? info->x_org + info->width/2 : parent->get_root_w(0)/2;
205         int cy = info ? info->y_org + info->height/2 : parent->get_root_h(0)/2;
206         int vx = viewing->get_vx(), rx = 0;
207         int vy = viewing->get_vy(), ry = 0;
208         wdw->get_root_coordinates(vx, vy, &rx, &ry);
209         rx += (rx >= cx ? -view_w : viewing->vw);
210         ry += (ry >= cy ? -view_h : viewing->vh);
211         ViewPopup *vwin = new ViewPopup(this, frame, rx, ry, view_w, view_h);
212         wdw->set_active_subwindow(vwin);
213         return vwin;
214 }
215
216 void VIconThread::
217 reset_images()
218 {
219         for( int i=t_heap.size(); --i>=0; ) t_heap[i]->reset();
220         timer->update();
221         img_dirty = win_dirty = 0;
222 }
223
224 void VIconThread::add_vicon(VIcon *vip)
225 {
226         double age = vip->age;
227         int i = t_heap.size();  t_heap.append(vip);
228         for( int k; i>0 && age<t_heap[(k=(i-1)/2)]->age; i=k )
229                 t_heap[i] = t_heap[k];
230         t_heap[i] = vip;
231 }
232
233 int VIconThread::del_vicon(VIcon *vicon)
234 {
235         int i = t_heap.size();
236         while( --i >= 0 && t_heap[i] != vicon );
237         if( i < 0 ) return 0;
238         remove_vicon(i);
239         return 1;
240 }
241
242 void VIconThread::draw_vframe(BC_WindowBase *wdw, VFrame *frame)
243 {
244         if( !wdw || !frame ) return;
245         wdw->draw_vframe(frame, 0,0, wdw->get_w(),wdw->get_h());
246 }
247
248 void VIconThread::set_view_popup(VIcon *vicon, VIconDrawVFrame *draw_vfrm)
249 {
250         this->vicon = vicon;
251         this->draw_vfrm = vicon && !draw_vfrm ? VIconThread::draw_vframe : draw_vfrm;
252 }
253
254 void VIconThread::close_view_popup()
255 {
256         set_view_popup(0);
257 }
258
259 int VIconThread::
260 update_view()
261 {
262         if( viewing ) viewing->stop_audio();
263         delete view_win;  view_win = 0;
264         if( (viewing=vicon) != 0 ) {
265                 VFrame *frame = viewing->frame();
266                 view_win = new_view_window(frame);
267                 view_win->show_window();
268                 vicon->start_audio();
269         }
270         wdw->set_active_subwindow(view_win);
271         return 1;
272 }
273
274
275 void VIconThread::
276 draw_images()
277 {
278         for( int i=0; i<t_heap.size(); ++i )
279                 draw(t_heap[i]);
280 }
281
282 void VIconThread::
283 flash()
284 {
285         if( !img_dirty && !win_dirty ) return;
286         if( img_dirty ) wdw->flash();
287         if( win_dirty && view_win ) view_win->flash();
288         win_dirty = img_dirty = 0;
289 }
290
291 int VIconThread::
292 draw(VIcon *vicon)
293 {
294         int x = vicon->get_vx(), y = vicon->get_vy();
295         int draw_img = visible(vicon, x, y);
296         int draw_win = view_win && viewing == vicon ? 1 : 0;
297         if( !draw_img && !draw_win ) return 0;
298         if( !vicon->frame() ) return 0;
299         if( draw_img ) {
300                 vicon->draw_vframe(this, wdw, x, y);
301                 img_dirty = 1;
302         }
303         if( draw_win ) {
304                 draw_vfrm(view_win, vicon->frame());
305                 win_dirty = 1;
306         }
307         return 1;
308 }
309
310 void VIconThread::hide_vicons(int v)
311 {
312         for( int i=0; i<t_heap.size(); ++i ) {
313                 t_heap[i]->hidden = v;
314                 t_heap[i]->age = 0;
315         }
316 }
317
318 void VIconThread::
319 run()
320 {
321         while(!done) {
322                 draw_lock->lock("VIconThread::run 0");
323                 if( done ) break;
324                 wdw->lock_window("BC_WindowBase::run 1");
325                 drawing_started();
326                 reset_images();
327                 int64_t seq_no = 0, now = 0;
328                 int64_t draw_flash = 1000 / refresh_rate;
329                 while( !interrupted ) {
330                         if( viewing != vicon )
331                                 update_view();
332                         VIcon *next = low_vicon();
333                         while( next && next->age < draw_flash ) {
334                                 now = timer->get_difference();
335                                 if( now >= draw_flash ) break;
336                                 draw(next);
337                                 if( !next->seq_no ) {
338                                         next->cycle_start = now;
339                                         if( next->playing_audio > 0 )
340                                                 next->start_audio();
341                                 }
342                                 int64_t ref_no = (now - next->cycle_start) / 1000. * refresh_rate;
343                                 int count = ref_no - next->seq_no;
344                                 if( count < 1 ) count = 1;
345                                 ref_no = next->seq_no + count;
346                                 next->age =  next->cycle_start + 1000. * ref_no / refresh_rate;
347                                 if( !next->set_seq_no(ref_no) )
348                                         next->age = now + 1000.;
349                                 add_vicon(next);
350                                 next = low_vicon();
351                         }
352                         if( !next ) break;
353                         add_vicon(next);
354                         if( draw_flash < now+1 )
355                                 draw_flash = now+1;
356                         wdw->unlock_window();
357                         while( !interrupted ) {
358                                 now = timer->get_difference();
359                                 int64_t ms = draw_flash - now;
360                                 if( ms <= 0 ) break;
361                                 if( ms > 100 ) ms = 100;
362                                 Timer::delay(ms);
363                         }
364                         wdw->lock_window("BC_WindowBase::run 2");
365                         now = timer->get_difference();
366                         int64_t late = now - draw_flash;
367                         if( late < 1000 ) flash();
368                         int64_t ref_no = now / 1000. * refresh_rate;
369                         int64_t count = ref_no - seq_no;
370                         if( count < 1 ) count = 1;
371                         seq_no += count;
372                         draw_flash = seq_no * 1000. / refresh_rate;
373                 }
374                 if( viewing != vicon )
375                         update_view();
376                 drawing_stopped();
377                 interrupted = -1;
378                 wdw->unlock_window();
379         }
380 }
381
382
383 void VIcon::init_audio(int audio_size)
384 {
385         this->audio_size = audio_size;
386         audio_data = new uint8_t[audio_size];
387         memset(audio_data, 0, audio_size);
388 }
389
390 void VIcon::dump(const char *dir)
391 {
392         mkdir(dir,0777);
393         for( int i=0; i<images.size(); ++i ) {
394                 char fn[1024];  sprintf(fn,"%s/img%05d.png",dir,i);
395                 printf("\r%s",fn);
396                 VFrame *img = *images[i];
397                 img->write_png(fn);
398         }
399         printf("\n");
400 }
401