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