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