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