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