add missing GPL information in guicast program files
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / canvas.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008-2017 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bcsignals.h"
23 #include "canvas.h"
24 #include "clip.h"
25 #include "edl.h"
26 #include "edlsession.h"
27 #include "keys.h"
28 #include "language.h"
29 #include "mainsession.h"
30 #include "mwindowgui.h"
31 #include "mutex.h"
32 #include "mwindow.h"
33 #include "playback3d.h"
34 #include "videodevice.h"
35 #include "vframe.h"
36
37
38
39 Canvas::Canvas(MWindow *mwindow, BC_WindowBase *subwindow,
40         int x, int y, int w, int h, int output_w, int output_h,
41         int use_scrollbars)
42 {
43         reset();
44
45         int xs10 = xS(10), ys10 = yS(10);
46         if(w < xs10) w = xs10;
47         if(h < ys10) h = ys10;
48         this->mwindow = mwindow;
49         this->subwindow = subwindow;
50         this->x = x;
51         this->y = y;
52         this->w = w;
53         this->h = h;
54         this->output_w = output_w;
55         this->output_h = output_h;
56         this->use_scrollbars = use_scrollbars;
57         this->canvas_auxwindow = 0;
58         this->scr_w0 = subwindow->get_screen_w(0, 0);
59         this->root_w = subwindow->get_root_w(0);
60         this->root_h = subwindow->get_root_h(0);
61         canvas_lock = new Condition(1, "Canvas::canvas_lock", 1);
62 }
63
64 Canvas::~Canvas()
65 {
66         if(refresh_frame) delete refresh_frame;
67         delete canvas_menu;
68         if(yscroll) delete yscroll;
69         if(xscroll) delete xscroll;
70         delete canvas_subwindow;
71         delete canvas_fullscreen;
72         delete canvas_lock;
73 }
74
75 void Canvas::reset()
76 {
77         use_scrollbars = 0;
78         output_w = 0;
79         output_h = 0;
80         xscroll = 0;
81         yscroll = 0;
82         refresh_frame = 0;
83         canvas_subwindow = 0;
84         canvas_fullscreen = 0;
85         is_processing = 0;
86         is_fullscreen = 0;
87         cursor_inside = 0;
88 }
89
90 BC_WindowBase *Canvas::lock_canvas(const char *loc)
91 {
92         canvas_lock->lock(loc);
93         BC_WindowBase *wdw = get_canvas();
94         if( wdw ) wdw->lock_window(loc);
95         return wdw;
96 }
97
98 void Canvas::unlock_canvas()
99 {
100         BC_WindowBase *wdw = get_canvas();
101         if( wdw ) wdw->unlock_window();
102         canvas_lock->unlock();
103 }
104
105 BC_WindowBase* Canvas::get_canvas()
106 {
107         if(get_fullscreen() && canvas_fullscreen)
108                 return canvas_fullscreen;
109         return canvas_auxwindow ? canvas_auxwindow : canvas_subwindow;
110 }
111
112 void Canvas::use_auxwindow(BC_WindowBase *aux)
113 {
114         canvas_auxwindow = aux;
115 }
116
117 int Canvas::get_fullscreen()
118 {
119         return is_fullscreen;
120 }
121
122 // Get dimensions given a zoom
123 void Canvas::calculate_sizes(float aspect_ratio,
124                 int output_w, int output_h, float zoom,
125                 int &w, int &h)
126 {
127 // Horizontal stretch
128         if( (float)output_w/output_h <= aspect_ratio ) {
129                 w = (float)output_h * aspect_ratio * zoom;
130                 h = (float)output_h * zoom;
131         }
132         else {
133 // Vertical stretch
134                 h = (float)output_w / aspect_ratio * zoom;
135                 w = (float)output_w * zoom;
136         }
137 }
138
139 float Canvas::get_x_offset(EDL *edl, int single_channel,
140                 float zoom_x, float conformed_w, float conformed_h)
141 {
142         if( use_scrollbars ) return get_xscroll();
143         int canvas_w = get_canvas()->get_w(), out_w = canvas_w;
144         int canvas_h = get_canvas()->get_h(), out_h = canvas_h;
145         float conformed_ratio = conformed_w/conformed_h;
146         if( (float)out_w/out_h > conformed_ratio )
147                 out_w = out_h * conformed_ratio + 0.5f;
148         float ret = out_w >= canvas_w ? 0 : -0.5f*(canvas_w - out_w) / zoom_x;
149         return ret;
150 }
151
152 float Canvas::get_y_offset(EDL *edl, int single_channel,
153                 float zoom_y, float conformed_w, float conformed_h)
154 {
155         if( use_scrollbars ) return get_yscroll();
156         int canvas_w = get_canvas()->get_w(), out_w = canvas_w;
157         int canvas_h = get_canvas()->get_h(), out_h = canvas_h;
158         float conformed_ratio = conformed_w/conformed_h;
159         if( (float)out_w/out_h <= conformed_ratio )
160                 out_h = out_w / conformed_ratio + 0.5f;
161         float ret = out_h >= canvas_h ? 0 : -0.5f*(canvas_h - out_h) / zoom_y;
162         return ret;
163 }
164
165 // This may not be used anymore
166 void Canvas::check_boundaries(EDL *edl, int &x, int &y, float &zoom)
167 {
168         if(x + w_visible > w_needed) x = w_needed - w_visible;
169         if(y + h_visible > h_needed) y = h_needed - h_visible;
170
171         if(x < 0) x = 0;
172         if(y < 0) y = 0;
173 }
174
175 void Canvas::update_scrollbars(EDL *edl, int flush)
176 {
177         if( edl )
178                 get_scrollbars(edl);
179         if( use_scrollbars ) {
180                 if( xscroll ) xscroll->update_length(w_needed, get_xscroll(), w_visible, flush);
181                 if( yscroll ) yscroll->update_length(h_needed, get_yscroll(), h_visible, flush);
182         }
183 }
184
185 float Canvas::get_auto_zoom(EDL *edl)
186 {
187         float conformed_w, conformed_h;
188         edl->calculate_conformed_dimensions(0, conformed_w, conformed_h);
189         BC_WindowBase *window = get_canvas();
190         int window_w = window ? window->get_w() : w;
191         int window_h = window ? window->get_h() : h;
192         float zoom_x = window_w / conformed_w;
193         float zoom_y = window_h / conformed_h;
194         return zoom_x < zoom_y ? zoom_x : zoom_y;
195 }
196 void Canvas::zoom_auto()
197 {
198         use_scrollbars = 0;
199 }
200
201 void Canvas::get_zooms(EDL *edl, int single_channel,
202                 float &zoom_x, float &zoom_y,
203                 float &conformed_w, float &conformed_h)
204 {
205         edl->calculate_conformed_dimensions(single_channel,
206                         conformed_w, conformed_h);
207
208         float zoom = get_zoom();
209         if( !use_scrollbars || !zoom ) zoom = get_auto_zoom(edl);
210         zoom_x = zoom * conformed_w / get_output_w(edl);
211         zoom_y = zoom * conformed_h / get_output_h(edl);
212 }
213
214 void Canvas::set_zoom(EDL *edl, float zoom)
215 {
216         BC_WindowBase *window = get_canvas();
217         int cw = window ? window->get_w() : w;
218         int ch = window ? window->get_h() : h;
219         float cx = 0.5f * cw, cy = 0.5f * ch;
220         set_zoom(edl, zoom, cx, cy);
221 }
222
223 void Canvas::set_zoom(EDL *edl, float zoom, float cx, float cy)
224 {
225         float output_x = cx, output_y = cy;
226         canvas_to_output(edl, 0, output_x, output_y);
227         update_zoom(0, 0, zoom);
228         float zoom_x, zoom_y, conformed_w, conformed_h;
229         get_zooms(edl, 0, zoom_x, zoom_y, conformed_w, conformed_h);
230         if( zoom ) {
231                 output_x -= cx / zoom_x;
232                 output_y -= cy / zoom_y;
233         }
234         else {
235                 output_x = get_x_offset(edl, 0, zoom_x, conformed_w, conformed_h);
236                 output_y = get_y_offset(edl, 0, zoom_y, conformed_w, conformed_h);
237         }
238         update_zoom(output_x, output_y, zoom);
239 }
240
241
242 // Convert a coordinate on the canvas to a coordinate on the output
243 void Canvas::canvas_to_output(EDL *edl, int single_channel, float &x, float &y)
244 {
245         float zoom_x, zoom_y, conformed_w, conformed_h;
246         get_zooms(edl, single_channel, zoom_x, zoom_y, conformed_w, conformed_h);
247
248 //printf("Canvas::canvas_to_output y=%f zoom_y=%f y_offset=%f\n",
249 //      y, zoom_y, get_y_offset(edl, single_channel, zoom_y, conformed_w, conformed_h));
250
251         x = (float)x / zoom_x + get_x_offset(edl, single_channel, zoom_x, conformed_w, conformed_h);
252         y = (float)y / zoom_y + get_y_offset(edl, single_channel, zoom_y, conformed_w, conformed_h);
253 }
254
255 void Canvas::output_to_canvas(EDL *edl, int single_channel, float &x, float &y)
256 {
257         float zoom_x, zoom_y, conformed_w, conformed_h;
258         get_zooms(edl, single_channel, zoom_x, zoom_y, conformed_w, conformed_h);
259
260 //printf("Canvas::output_to_canvas x=%f zoom_x=%f x_offset=%f\n", x, zoom_x, get_x_offset(edl, single_channel, zoom_x, conformed_w));
261
262         x = (float)zoom_x * (x - get_x_offset(edl, single_channel, zoom_x, conformed_w, conformed_h));
263         y = (float)zoom_y * (y - get_y_offset(edl, single_channel, zoom_y, conformed_w, conformed_h));
264 }
265
266
267
268 void Canvas::get_transfers(EDL *edl,
269         float &output_x1, float &output_y1, float &output_x2, float &output_y2,
270         float &canvas_x1, float &canvas_y1, float &canvas_x2, float &canvas_y2,
271         int canvas_w, int canvas_h)
272 {
273 //printf("Canvas::get_transfers %d canvas_w=%d canvas_h=%d\n", 
274 // __LINE__,  canvas_w, canvas_h);
275 // automatic canvas size detection
276         if( canvas_w < 0 ) canvas_w = get_canvas()->get_w();
277         if( canvas_h < 0 ) canvas_h = get_canvas()->get_h();
278
279         float in_x1, in_y1, in_x2, in_y2;
280         float out_x1, out_y1, out_x2, out_y2;
281         float zoom_x, zoom_y, conformed_w, conformed_h;
282
283         get_zooms(edl, 0, zoom_x, zoom_y, conformed_w, conformed_h);
284         out_x1 = 0;  out_x2 = canvas_w;
285         out_y1 = 0;  out_y2 = canvas_h;
286         in_x1 = 0;   in_x2  = canvas_w;
287         in_y1 = 0;   in_y2  = canvas_h;
288         canvas_to_output(edl, 0, in_x1, in_y1);
289         canvas_to_output(edl, 0, in_x2, in_y2);
290
291         if( in_x1 < 0 ) {
292                 out_x1 += -in_x1 * zoom_x;
293                 in_x1 = 0;
294         }
295
296         if( in_y1 < 0 ) {
297                 out_y1 += -in_y1 * zoom_y;
298                 in_y1 = 0;
299         }
300
301         int output_w = get_output_w(edl);
302         int output_h = get_output_h(edl);
303         if( in_x2 > output_w ) {
304                 out_x2 -= (in_x2 - output_w) * zoom_x;
305                 in_x2 = output_w;
306         }
307
308         if( in_y2 > output_h ) {
309                 out_y2 -= (in_y2 - output_h) * zoom_y;
310                 in_y2 = output_h;
311         }
312
313         output_x1 = in_x1;   output_x2 = in_x2;
314         output_y1 = in_y1;   output_y2 = in_y2;
315         canvas_x1 = out_x1;  canvas_x2 = out_x2;
316         canvas_y1 = out_y1;  canvas_y2 = out_y2;
317
318 // Clamp to minimum value
319         if( output_x1 < 0 ) output_x1 = 0;
320         if( output_x2 < output_x1 ) output_x2 = output_x1;
321         if( canvas_x1 < 0 ) canvas_x1 = 0;
322         if( canvas_x2 < canvas_x1 ) canvas_x2 = canvas_x1;
323 // printf("Canvas::get_transfers %d %f,%f %f,%f -> %f,%f %f,%f\n", __LINE__,
324 //  output_x1, output_y1, output_x2, output_y2,
325 //  canvas_x1, canvas_y1, canvas_x2, canvas_y2);
326 }
327
328 int Canvas::scrollbars_exist()
329 {
330         return use_scrollbars && (xscroll || yscroll);
331 }
332
333 int Canvas::get_output_w(EDL *edl)
334 {
335         return edl->session->output_w;
336 }
337
338 int Canvas::get_output_h(EDL *edl)
339 {
340         return edl->session->output_h;
341 }
342
343
344 int Canvas::get_scrollbars(EDL *edl)
345 {
346         int ret = 0;
347         BC_WindowBase *window = get_canvas();
348         if( !window ) use_scrollbars = 0;
349         int canvas_w = w, canvas_h = h;
350
351         w_needed = w_visible = edl ? edl->session->output_w : view_w;
352         h_needed = h_visible = edl ? edl->session->output_h : view_h;
353
354         int need_xscroll = 0, need_yscroll = 0;
355         if( edl && use_scrollbars ) {
356                 float zoom_x, zoom_y, conformed_w, conformed_h;
357                 get_zooms(edl, 0, zoom_x, zoom_y, conformed_w, conformed_h);
358                 w_visible = canvas_w / zoom_x;
359                 h_visible = canvas_h / zoom_y;
360                 float output_x = 0, output_y = 0;
361                 output_to_canvas(edl, 0, output_x, output_y);
362                 if( output_x < 0 ) need_xscroll = 1;
363                 if( output_y < 0 ) need_yscroll = 1;
364                 output_x = w_needed, output_y = h_needed;
365                 output_to_canvas(edl, 0, output_x, output_y);
366                 if( output_x > canvas_w ) need_xscroll = 1;
367                 if( output_y > canvas_h ) need_yscroll = 1;
368                 if( need_xscroll ) {
369                         canvas_h -= BC_ScrollBar::get_span(SCROLL_HORIZ);
370                         h_visible = canvas_h / zoom_y;
371                 }
372                 if( need_yscroll ) {
373                         canvas_w -= BC_ScrollBar::get_span(SCROLL_VERT);
374                         w_visible = canvas_w / zoom_x;
375                 }
376                 view_w = canvas_w;
377                 view_h = canvas_h;
378         }
379
380         if( need_xscroll ) {
381                 if( !xscroll ) {
382                         xscroll = new CanvasXScroll(edl, this, view_x, view_y + view_h,
383                                         w_needed, get_xscroll(), w_visible, view_w);
384                         subwindow->add_subwindow(xscroll);
385                         xscroll->show_window(0);
386                 }
387                 else
388                         xscroll->reposition_window(view_x, view_y + view_h, view_w);
389
390                 if( xscroll->get_length() != w_needed ||
391                     xscroll->get_handlelength() != w_visible )
392                         xscroll->update_length(w_needed, get_xscroll(), w_visible, 0);
393                 ret = 1;
394         }
395         else if( xscroll ) {
396                 delete xscroll;  xscroll = 0;
397                 ret = 1;
398         }
399
400         if( need_yscroll ) {
401                 if( !yscroll ) {
402                         yscroll = new CanvasYScroll(edl, this, view_x + view_w, view_y,
403                                         h_needed, get_yscroll(), h_visible, view_h);
404                         subwindow->add_subwindow(yscroll);
405                         yscroll->show_window(0);
406                 }
407                 else
408                         yscroll->reposition_window(view_x + view_w, view_y, view_h);
409
410                 if( yscroll->get_length() != edl->session->output_h ||
411                     yscroll->get_handlelength() != h_visible )
412                         yscroll->update_length(h_needed, get_yscroll(), h_visible, 0);
413                 ret = 1;
414         }
415         else if( yscroll ) {
416                 delete yscroll;  yscroll = 0;
417                 ret = 1;
418         }
419         return ret;
420 }
421
422
423 void Canvas::update_geometry(EDL *edl, int x, int y, int w, int h)
424 {
425         int redraw = 0;
426         if( this->x != x || this->y != y ||
427             this->w != w || this->h != h )
428                 redraw = 1;
429         if( !redraw )
430                 redraw = get_scrollbars(edl);
431         if( redraw )
432                 reposition_window(edl, x, y, w, h);
433 }
434
435 void Canvas::reposition_window(EDL *edl, int x, int y, int w, int h)
436 {
437         this->x = view_x = x;  this->y = view_y = y;
438         this->w = view_w = w;  this->h = view_h = h;
439 //printf("Canvas::reposition_window 1\n");
440         get_scrollbars(edl);
441 //printf("Canvas::reposition_window %d %d %d %d\n", view_x, view_y, view_w, view_h);
442         if( canvas_subwindow ) {
443                 canvas_subwindow->reposition_window(view_x, view_y, view_w, view_h);
444
445 // Need to clear out the garbage in the back
446                 if( canvas_subwindow->get_video_on() )
447                         clear_borders(edl);
448         }
449         refresh(0);
450 }
451
452 // must hold window lock
453 int Canvas::refresh(int flush)
454 {
455         BC_WindowBase *window = get_canvas();
456         if( !window ) return 0;
457 // relock in lock order to prevent deadlock
458         window->unlock_window();
459         lock_canvas("Canvas::refresh");
460         draw_refresh(flush);
461         canvas_lock->unlock();
462         return 1;
463 }
464 // must not hold locks
465 int Canvas::redraw(int flush)
466 {
467         lock_canvas("Canvas::redraw");
468         draw_refresh(flush);
469         unlock_canvas();
470         return 1;
471 }
472
473 void Canvas::set_cursor(int cursor)
474 {
475         get_canvas()->set_cursor(cursor, 0, 1);
476 }
477
478 int Canvas::get_cursor_x()
479 {
480         return get_canvas()->get_cursor_x();
481 }
482
483 int Canvas::get_cursor_y()
484 {
485         return get_canvas()->get_cursor_y();
486 }
487
488 int Canvas::get_buttonpress()
489 {
490         return get_canvas()->get_buttonpress();
491 }
492
493
494 void Canvas::create_objects(EDL *edl)
495 {
496         view_x = x;  view_y = y;
497         view_w = w;  view_h = h;
498         get_scrollbars(edl);
499
500         subwindow->unlock_window();
501         create_canvas();
502         subwindow->lock_window("Canvas::create_objects");
503
504         subwindow->add_subwindow(canvas_menu = new CanvasPopup(this));
505         canvas_menu->create_objects();
506
507         subwindow->add_subwindow(fullscreen_menu = new CanvasFullScreenPopup(this));
508         fullscreen_menu->create_objects();
509
510 }
511
512 int Canvas::button_press_event()
513 {
514         int result = 0;
515
516         if(get_canvas()->get_buttonpress() == 3)
517         {
518                 if(get_fullscreen())
519                         fullscreen_menu->activate_menu();
520                 else
521                         canvas_menu->activate_menu();
522                 result = 1;
523         }
524
525         return result;
526 }
527
528 void Canvas::start_single()
529 {
530         is_processing = 1;
531         status_event();
532 }
533
534 void Canvas::stop_single()
535 {
536         is_processing = 0;
537         status_event();
538 }
539
540 void Canvas::start_video()
541 {
542         if(get_canvas())
543         {
544                 get_canvas()->start_video();
545                 status_event();
546         }
547 }
548
549 void Canvas::stop_video()
550 {
551         if(get_canvas())
552         {
553                 get_canvas()->stop_video();
554                 status_event();
555         }
556 }
557
558 int Canvas::set_fullscreen(int on, int unlock)
559 {
560         int ret = 0;
561         BC_WindowBase *window = get_canvas();
562         if( unlock )
563                 window->unlock_window();
564         if( on && !get_fullscreen() ) {
565                 start_fullscreen();
566                 ret = 1;
567         }
568         if( !on && get_fullscreen() ) {
569                 stop_fullscreen();
570                 ret = 1;
571         }
572         if( unlock )
573                 window->lock_window("Canvas::set_fullscreen");
574         return ret;
575 }
576
577 void Canvas::start_fullscreen()
578 {
579         is_fullscreen = 1;
580         create_canvas();
581 }
582
583 void Canvas::stop_fullscreen()
584 {
585         is_fullscreen = 0;
586         create_canvas();
587 }
588
589 void Canvas::create_canvas()
590 {
591         canvas_lock->lock("Canvas::create_canvas");
592         int video_on = 0;
593         BC_WindowBase *wdw = 0;
594         if( !get_fullscreen() ) {
595 // Enter windowed
596                 if( canvas_fullscreen ) {
597                         canvas_fullscreen->lock_window("Canvas::create_canvas 1");
598                         video_on = canvas_fullscreen->get_video_on();
599                         if( video_on ) canvas_fullscreen->stop_video();
600                         canvas_fullscreen->hide_window();
601                         canvas_fullscreen->unlock_window();
602                 }
603                 if( !canvas_auxwindow && !canvas_subwindow ) {
604                         subwindow->add_subwindow(canvas_subwindow = new CanvasOutput(this,
605                                 view_x, view_y, view_w, view_h));
606                 }
607                 wdw = get_canvas();
608                 wdw->lock_window("Canvas::create_canvas 2");
609         }
610         else {
611 // Enter fullscreen
612                 wdw = canvas_auxwindow ? canvas_auxwindow : canvas_subwindow;
613                 wdw->lock_window("Canvas::create_canvas 3");
614                 video_on = wdw->get_video_on();
615                 if( video_on ) wdw->stop_video();
616                 int x, y, w, h;
617                 wdw->get_fullscreen_geometry(x, y, w, h);
618                 wdw->unlock_window();
619                 if( canvas_fullscreen ) {
620                         if( x != canvas_fullscreen->get_x() ||
621                             y != canvas_fullscreen->get_y() ||
622                             w != canvas_fullscreen->get_w() ||
623                             h != canvas_fullscreen->get_h() ) {
624                                 delete canvas_fullscreen;
625                                 canvas_fullscreen = 0;
626                         }
627                 }
628                 if( !canvas_fullscreen )
629                         canvas_fullscreen = new CanvasFullScreen(this, w, h);
630                 wdw = canvas_fullscreen;
631                 wdw->lock_window("Canvas::create_canvas 4");
632                 wdw->show_window();
633                 wdw->sync_display();
634                 wdw->reposition_window(x, y);
635         }
636
637         if( video_on )
638                 wdw->start_video();
639         else
640                 draw_refresh(1);
641
642         wdw->focus();
643         wdw->unlock_window();
644         canvas_lock->unlock();
645 }
646
647
648 int Canvas::cursor_leave_event_base(BC_WindowBase *caller)
649 {
650         int result = 0;
651         if(cursor_inside) result = cursor_leave_event();
652         cursor_inside = 0;
653         return result;
654 }
655
656 int Canvas::cursor_enter_event_base(BC_WindowBase *caller)
657 {
658         int result = 0;
659         if(caller->is_event_win() && caller->cursor_inside())
660         {
661                 cursor_inside = 1;
662                 result = cursor_enter_event();
663         }
664         return result;
665 }
666
667 int Canvas::button_press_event_base(BC_WindowBase *caller)
668 {
669         if(caller->is_event_win() && caller->cursor_inside())
670         {
671                 return button_press_event();
672         }
673         return 0;
674 }
675
676 int Canvas::keypress_event(BC_WindowBase *caller)
677 {
678         int key = caller->get_keypress();
679         switch( key ) {
680         case 'f': {
681                 int on = get_fullscreen() ? 0 : 1;
682                 set_fullscreen(on, 1);
683                 break; }
684         case ESC:
685                 set_fullscreen(0, 1);
686                 break;
687         default:
688                 return caller->context_help_check_and_show();
689         }
690         return 1;
691 }
692
693 // process_scope uses the refresh frame for opengl
694 void Canvas::update_refresh(VideoDevice *device, VFrame *output_frame)
695 {
696         int best_color_model = output_frame->get_color_model();
697         int use_opengl =
698                 device->out_config->driver == PLAYBACK_X11_GL &&
699                 output_frame->get_opengl_state() != VFrame::RAM;
700
701 // OpenGL does YUV->RGB in the compositing step
702         if( use_opengl )
703                 best_color_model = BC_RGB888;
704         else if( BC_CModels::has_alpha(best_color_model) ) {
705                 best_color_model =
706                         BC_CModels::is_float(best_color_model ) ?
707                                 BC_RGB_FLOAT :
708                         BC_CModels::is_yuv(best_color_model ) ?
709                                 ( BC_CModels::calculate_pixelsize(best_color_model) > 8 ?
710                                         BC_YUV161616 : BC_YUV888 ) :
711                                 ( BC_CModels::calculate_pixelsize(best_color_model) > 8 ?
712                                         BC_RGB161616 : BC_RGB888 ) ;
713         }
714         int out_w = output_frame->get_w();
715         int out_h = output_frame->get_h();
716         if( refresh_frame &&
717            (refresh_frame->get_w() != out_w ||
718             refresh_frame->get_h() != out_h ||
719             refresh_frame->get_color_model() != best_color_model ) ) {
720 // x11 direct render uses BC_BGR8888, use tranfer_from to remap
721                 delete refresh_frame;  refresh_frame = 0;
722         }
723
724         if( !refresh_frame ) {
725                 refresh_frame =
726                         new VFrame(out_w, out_h, best_color_model);
727         }
728
729         if( use_opengl ) {
730                 unlock_canvas();
731                 mwindow->playback_3d->copy_from(this, refresh_frame, output_frame, 0);
732                 lock_canvas("Canvas::update_refresh");
733         }
734         else
735                 refresh_frame->transfer_from(output_frame, -1);
736         draw_scope(refresh_frame, 1);
737 }
738
739 void Canvas::process_scope(VideoDevice *video, VFrame *frame)
740 {
741         if( !scope_on() ) return;
742         int use_opengl =
743                 video->out_config->driver == PLAYBACK_X11_GL &&
744                 frame->get_opengl_state() != VFrame::RAM;
745         if( use_opengl ) {
746                 update_refresh(video, frame);
747                 frame = refresh_frame;
748         }
749         if( frame )
750                 draw_scope(frame, 0);
751 }
752
753 void Canvas::clear(int flash)
754 {
755         BC_WindowBase *window = get_canvas();
756         if( !window ) return;
757         window->set_bg_color(get_clear_color());
758         window->clear_box(0,0, window->get_w(), window->get_h());
759         if( flash ) window->flash();
760 }
761
762 void Canvas::clear_borders(EDL *edl)
763 {
764         BC_WindowBase *window = get_canvas();
765         if( !window ) return;
766         int window_w = window->get_w();
767         int window_h = window->get_h();
768         int color = get_clear_color();
769         window->set_color(color);
770
771         if( !edl ) {
772                 window->draw_box(0, 0, window_w, window_h);
773                 window->flash(0);
774                 return;
775         }
776
777         float output_x1,output_y1, output_x2,output_y2;
778         float canvas_x1,canvas_y1, canvas_x2,canvas_y2;
779         get_transfers(edl,
780                 output_x1, output_y1, output_x2, output_y2,
781                 canvas_x1, canvas_y1, canvas_x2, canvas_y2);
782
783         if( canvas_y1 > 0 ) {
784                 window->draw_box(0, 0, window_w, canvas_y1);
785                 window->flash(0, 0, window_w, canvas_y1);
786         }
787
788         if( canvas_y2 < window_h ) {
789                 window->draw_box(0, canvas_y2, window_w, window_h-canvas_y2);
790                 window->flash(0, canvas_y2, window_w, window_h-canvas_y2);
791         }
792
793         if( canvas_x1 > 0 ) {
794                 window->draw_box(0, canvas_y1, canvas_x1, canvas_y2-canvas_y1);
795                 window->flash(0, canvas_y1, canvas_x1, canvas_y2-canvas_y1);
796         }
797
798         if( canvas_x2 < window_w ) {
799                 window->draw_box(canvas_x2, canvas_y1,
800                         window_w-canvas_x2, canvas_y2-canvas_y1);
801                 window->flash(canvas_x2, canvas_y1,
802                         window_w-canvas_x2, canvas_y2-canvas_y1);
803         }
804 }
805
806 int Canvas::get_clear_color()
807 {
808         BC_WindowBase *cwdw = get_canvas();
809         if( !cwdw ) return 0;
810         return cwdw->get_bg_color();
811 }
812
813 CanvasOutput::CanvasOutput(Canvas *canvas,
814     int x, int y, int w, int h)
815  : BC_SubWindow(x, y, w, h, canvas->get_clear_color())
816 {
817         this->canvas = canvas;
818 }
819
820 CanvasOutput::~CanvasOutput()
821 {
822 }
823
824 int CanvasOutput::cursor_leave_event()
825 {
826         return canvas->cursor_leave_event_base(canvas->get_canvas());
827 }
828
829 int CanvasOutput::cursor_enter_event()
830 {
831         return canvas->cursor_enter_event_base(canvas->get_canvas());
832 }
833
834 int CanvasOutput::button_press_event()
835 {
836         return canvas->button_press_event_base(canvas->get_canvas());
837 }
838
839 int CanvasOutput::button_release_event()
840 {
841         return canvas->button_release_event();
842 }
843
844 int CanvasOutput::cursor_motion_event()
845 {
846         return canvas->cursor_motion_event();
847 }
848
849 int CanvasOutput::keypress_event()
850 {
851         return canvas->keypress_event(canvas->get_canvas());
852 }
853
854
855
856 CanvasFullScreen::CanvasFullScreen(Canvas *canvas, int w, int h)
857  : BC_FullScreen(canvas->subwindow, w, h, canvas->get_clear_color(), 0, 0, 0)
858 {
859         this->canvas = canvas;
860 }
861
862 CanvasFullScreen::~CanvasFullScreen()
863 {
864 }
865
866
867 CanvasXScroll::CanvasXScroll(EDL *edl, Canvas *canvas, int x, int y,
868         int length, int position, int handle_length, int pixels)
869  : BC_ScrollBar(x, y, SCROLL_HORIZ, pixels, length, position, handle_length)
870 {
871         this->canvas = canvas;
872 }
873
874 CanvasXScroll::~CanvasXScroll()
875 {
876 }
877
878 int CanvasXScroll::handle_event()
879 {
880         canvas->update_zoom(get_value(), canvas->get_yscroll(),
881                         canvas->get_zoom());
882         return canvas->refresh(1);
883 }
884
885
886 CanvasYScroll::CanvasYScroll(EDL *edl, Canvas *canvas, int x, int y,
887         int length, int position, int handle_length, int pixels)
888  : BC_ScrollBar(x, y, SCROLL_VERT, pixels, length, position, handle_length)
889 {
890         this->canvas = canvas;
891 }
892
893 CanvasYScroll::~CanvasYScroll()
894 {
895 }
896
897 int CanvasYScroll::handle_event()
898 {
899         canvas->update_zoom(canvas->get_xscroll(), get_value(),
900                         canvas->get_zoom());
901         return canvas->refresh(1);
902 }
903
904
905 CanvasFullScreenPopup::CanvasFullScreenPopup(Canvas *canvas)
906  : BC_PopupMenu(0, 0, 0, "", 0)
907 {
908         this->canvas = canvas;
909 }
910
911
912 void CanvasFullScreenPopup::create_objects()
913 {
914         add_item(new CanvasSubWindowItem(canvas));
915 }
916
917 CanvasSubWindowItem::CanvasSubWindowItem(Canvas *canvas)
918  : BC_MenuItem(_("Windowed"), "f", 'f')
919 {
920         this->canvas = canvas;
921 }
922
923 int CanvasSubWindowItem::handle_event()
924 {
925 // It isn't a problem to delete the canvas from in here because the event
926 // dispatcher is the canvas subwindow.
927         canvas->set_fullscreen(0, 1);
928         return 1;
929 }
930
931
932 CanvasPopup::CanvasPopup(Canvas *canvas)
933  : BC_PopupMenu(0, 0, 0, "", 0)
934 {
935         this->canvas = canvas;
936 }
937
938 CanvasPopup::~CanvasPopup()
939 {
940 }
941
942 CanvasZoomSize::CanvasZoomSize(Canvas *canvas)
943  : BC_MenuItem(_("Resize Window..."))
944 {
945         this->canvas = canvas;
946 }
947
948 CanvasSizeSubMenu::CanvasSizeSubMenu(CanvasZoomSize *zoom_size)
949 {
950         this->zoom_size = zoom_size;
951 }
952
953 void CanvasPopup::create_objects()
954 {
955         add_item(new BC_MenuItem("-"));
956         add_item(new CanvasFullScreenItem(canvas));
957
958         CanvasZoomSize *zoom_size = new CanvasZoomSize(canvas);
959         add_item(zoom_size);
960         CanvasSizeSubMenu *submenu = new CanvasSizeSubMenu(zoom_size);
961         zoom_size->add_submenu(submenu);
962
963         submenu->add_submenuitem(new CanvasPopupSize(canvas, _("Zoom 25%"), 0.25));
964         submenu->add_submenuitem(new CanvasPopupSize(canvas, _("Zoom 33%"), 0.33));
965         submenu->add_submenuitem(new CanvasPopupSize(canvas, _("Zoom 50%"), 0.5));
966         submenu->add_submenuitem(new CanvasPopupSize(canvas, _("Zoom 75%"), 0.75));
967         submenu->add_submenuitem(new CanvasPopupSize(canvas, _("Zoom 100%"), 1.0));
968         submenu->add_submenuitem(new CanvasPopupSize(canvas, _("Zoom 150%"), 1.5));
969         submenu->add_submenuitem(new CanvasPopupSize(canvas, _("Zoom 200%"), 2.0));
970         submenu->add_submenuitem(new CanvasPopupSize(canvas, _("Zoom 300%"), 3.0));
971         submenu->add_submenuitem(new CanvasPopupSize(canvas, _("Zoom 400%"), 4.0));
972 }
973
974
975 CanvasPopupAuto::CanvasPopupAuto(Canvas *canvas)
976  : BC_MenuItem(_("Zoom Auto"))
977 {
978         this->canvas = canvas;
979 }
980
981 int CanvasPopupAuto::handle_event()
982 {
983         canvas->zoom_auto();
984         return 1;
985 }
986
987
988 CanvasPopupSize::CanvasPopupSize(Canvas *canvas, char *text, float percentage)
989  : BC_MenuItem(text)
990 {
991         this->canvas = canvas;
992         this->percentage = percentage;
993 }
994 CanvasPopupSize::~CanvasPopupSize()
995 {
996 }
997 int CanvasPopupSize::handle_event()
998 {
999         canvas->zoom_resize_window(percentage);
1000         return 1;
1001 }
1002
1003
1004 CanvasPopupResetCamera::CanvasPopupResetCamera(Canvas *canvas)
1005  : BC_MenuItem(_("Reset camera"), _("F11"), KEY_F11)
1006 {
1007         this->canvas = canvas;
1008 }
1009 int CanvasPopupResetCamera::handle_event()
1010 {
1011         canvas->reset_camera();
1012         return 1;
1013 }
1014
1015 CanvasPopupResetProjector::CanvasPopupResetProjector(Canvas *canvas)
1016  : BC_MenuItem(_("Reset projector"), _("F12"), KEY_F12)
1017 {
1018         this->canvas = canvas;
1019 }
1020 int CanvasPopupResetProjector::handle_event()
1021 {
1022         canvas->reset_projector();
1023         return 1;
1024 }
1025
1026
1027 CanvasPopupCameraKeyframe::CanvasPopupCameraKeyframe(Canvas *canvas)
1028  : BC_MenuItem(_("Camera keyframe"), _("Shift-F11"), KEY_F11)
1029 {
1030         this->canvas = canvas;
1031         set_shift(1);
1032 }
1033 int CanvasPopupCameraKeyframe::handle_event()
1034 {
1035         canvas->camera_keyframe();
1036         return 1;
1037 }
1038
1039 CanvasPopupProjectorKeyframe::CanvasPopupProjectorKeyframe(Canvas *canvas)
1040  : BC_MenuItem(_("Projector keyframe"), _("Shift-F12"), KEY_F12)
1041 {
1042         this->canvas = canvas;
1043         set_shift(1);
1044 }
1045 int CanvasPopupProjectorKeyframe::handle_event()
1046 {
1047         canvas->projector_keyframe();
1048         return 1;
1049 }
1050
1051
1052 CanvasPopupResetTranslation::CanvasPopupResetTranslation(Canvas *canvas)
1053  : BC_MenuItem(_("Reset translation"))
1054 {
1055         this->canvas = canvas;
1056 }
1057 int CanvasPopupResetTranslation::handle_event()
1058 {
1059         canvas->reset_translation();
1060         return 1;
1061 }
1062
1063
1064 CanvasFullScreenItem::CanvasFullScreenItem(Canvas *canvas)
1065  : BC_MenuItem(_("Fullscreen"), "f", 'f')
1066 {
1067         this->canvas = canvas;
1068 }
1069 int CanvasFullScreenItem::handle_event()
1070 {
1071         canvas->set_fullscreen(1, 1);
1072         return 1;
1073 }
1074
1075
1076 CanvasPopupRemoveSource::CanvasPopupRemoveSource(Canvas *canvas)
1077  : BC_MenuItem(_("Close source"))
1078 {
1079         this->canvas = canvas;
1080 }
1081 int CanvasPopupRemoveSource::handle_event()
1082 {
1083         canvas->close_source();
1084         return 1;
1085 }
1086
1087