tweak zoom/fullscr to remember cwdw scale after fullscr
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / canvas.C
index a01d1b9eabf3a11350f9c879cf59f9583df05116..e3e1b0f3381d2d826fe1a16f7b3aa7ce0df1ce05 100644 (file)
 
 
 
-Canvas::Canvas(MWindow *mwindow,
-       BC_WindowBase *subwindow,
-       int x,
-       int y,
-       int w,
-       int h,
-       int output_w,
-       int output_h,
+Canvas::Canvas(MWindow *mwindow, BC_WindowBase *subwindow,
+       int x, int y, int w, int h, int output_w, int output_h,
        int use_scrollbars)
 {
        reset();
 
-       if(w < 10) w = 10;
-       if(h < 10) h = 10;
+       int xs10 = xS(10), ys10 = yS(10);
+       if(w < xs10) w = xs10;
+       if(h < ys10) h = ys10;
        this->mwindow = mwindow;
        this->subwindow = subwindow;
        this->x = x;
@@ -63,7 +58,7 @@ Canvas::Canvas(MWindow *mwindow,
        this->scr_w0 = subwindow->get_screen_w(0, 0);
        this->root_w = subwindow->get_root_w(0);
        this->root_h = subwindow->get_root_h(0);
-       canvas_lock = new Mutex("Canvas::canvas_lock", 1);
+       canvas_lock = new Condition(1, "Canvas::canvas_lock", 1);
 }
 
 Canvas::~Canvas()
@@ -92,22 +87,21 @@ void Canvas::reset()
        cursor_inside = 0;
 }
 
-void Canvas::lock_canvas(const char *location)
+BC_WindowBase *Canvas::lock_canvas(const char *loc)
 {
-       canvas_lock->lock(location);
+       canvas_lock->lock(loc);
+       BC_WindowBase *wdw = get_canvas();
+       if( wdw ) wdw->lock_window(loc);
+       return wdw;
 }
 
 void Canvas::unlock_canvas()
 {
+       BC_WindowBase *wdw = get_canvas();
+       if( wdw ) wdw->unlock_window();
        canvas_lock->unlock();
 }
 
-int Canvas::is_locked()
-{
-       return canvas_lock->is_locked();
-}
-
-
 BC_WindowBase* Canvas::get_canvas()
 {
        if(get_fullscreen() && canvas_fullscreen)
@@ -141,112 +135,47 @@ int Canvas::get_fullscreen()
        return is_fullscreen;
 }
 
-void Canvas::set_fullscreen(int value)
-{
-       is_fullscreen = value;
-}
-
 // Get dimensions given a zoom
 void Canvas::calculate_sizes(float aspect_ratio,
-       int output_w,
-       int output_h,
-       float zoom,
-       int &w,
-       int &h)
+               int output_w, int output_h, float zoom,
+               int &w, int &h)
 {
 // Horizontal stretch
-       if((float)output_w / output_h <= aspect_ratio)
-       {
-               w = (int)((float)output_h * aspect_ratio * zoom);
-               h = (int)((float)output_h * zoom);
+       if( (float)output_w/output_h <= aspect_ratio ) {
+               w = (float)output_h * aspect_ratio * zoom;
+               h = (float)output_h * zoom;
        }
-       else
+       else {
 // Vertical stretch
-       {
-               h = (int)((float)output_w / aspect_ratio * zoom);
-               w = (int)((float)output_w * zoom);
+               h = (float)output_w / aspect_ratio * zoom;
+               w = (float)output_w * zoom;
        }
 }
 
-float Canvas::get_x_offset(EDL *edl,
-       int single_channel,
-       float zoom_x,
-       float conformed_w,
-       float conformed_h)
+float Canvas::get_x_offset(EDL *edl, int single_channel,
+               float zoom_x, float conformed_w, float conformed_h)
 {
-       if(use_scrollbars)
-       {
-               if(xscroll)
-               {
-// If the projection is smaller than the canvas, this forces it in the center.
-//                     if(conformed_w < w_visible)
-//                             return -(float)(w_visible - conformed_w) / 2;
-
-                       return (float)get_xscroll();
-               }
-               else
-                       return ((float)-get_canvas()->get_w() / zoom_x +
-                               edl->session->output_w) / 2;
-       }
-       else
-       {
-               int out_w, out_h;
-               int canvas_w = get_canvas()->get_w();
-               int canvas_h = get_canvas()->get_h();
-               out_w = canvas_w;
-               out_h = canvas_h;
-
-               if((float)out_w / out_h > conformed_w / conformed_h)
-               {
-                       out_w = (int)(out_h * conformed_w / conformed_h + 0.5);
-               }
-
-               if(out_w < canvas_w)
-                       return -(canvas_w - out_w) / 2 / zoom_x;
-       }
-
-       return 0;
+       if( use_scrollbars ) return get_xscroll();
+       int canvas_w = get_canvas()->get_w(), out_w = canvas_w;
+       int canvas_h = get_canvas()->get_h(), out_h = canvas_h;
+       float conformed_ratio = conformed_w/conformed_h;
+       if( (float)out_w/out_h > conformed_ratio )
+               out_w = out_h * conformed_ratio + 0.5f;
+       float ret = out_w >= canvas_w ? 0 : -0.5f*(canvas_w - out_w) / zoom_x;
+       return ret;
 }
 
-float Canvas::get_y_offset(EDL *edl,
-       int single_channel,
-       float zoom_y,
-       float conformed_w,
-       float conformed_h)
+float Canvas::get_y_offset(EDL *edl, int single_channel,
+               float zoom_y, float conformed_w, float conformed_h)
 {
-       if(use_scrollbars)
-       {
-               if(yscroll)
-               {
-// If the projection is smaller than the canvas, this forces it in the center.
-//                     if(conformed_h < h_visible)
-//                             return -(float)(h_visible - conformed_h) / 2;
-
-                       return (float)get_yscroll();
-               }
-               else
-                       return ((float)-get_canvas()->get_h() / zoom_y +
-                               edl->session->output_h) / 2;
-       }
-       else
-       {
-               int out_w, out_h;
-               int canvas_w = get_canvas()->get_w();
-               int canvas_h = get_canvas()->get_h();
-               out_w = canvas_w;
-               out_h = canvas_h;
-
-               if((float)out_w / out_h <= conformed_w / conformed_h)
-               {
-                       out_h = (int)((float)out_w / (conformed_w / conformed_h) + 0.5);
-               }
-
-//printf("Canvas::get_y_offset 1 %d %d %f\n", out_h, canvas_h, -((float)canvas_h - out_h) / 2);
-               if(out_h < canvas_h)
-                       return -((float)canvas_h - out_h) / 2 / zoom_y;
-       }
-
-       return 0;
+       if( use_scrollbars ) return get_yscroll();
+       int canvas_w = get_canvas()->get_w(), out_w = canvas_w;
+       int canvas_h = get_canvas()->get_h(), out_h = canvas_h;
+       float conformed_ratio = conformed_w/conformed_h;
+       if( (float)out_w/out_h <= conformed_ratio )
+               out_h = out_w / conformed_ratio + 0.5f;
+       float ret = out_h >= canvas_h ? 0 : -0.5f*(canvas_h - out_h) / zoom_y;
+       return ret;
 }
 
 // This may not be used anymore
@@ -259,59 +188,73 @@ void Canvas::check_boundaries(EDL *edl, int &x, int &y, float &zoom)
        if(y < 0) y = 0;
 }
 
-void Canvas::update_scrollbars(int flush)
+void Canvas::update_scrollbars(EDL *edl, int flush)
 {
-       if(use_scrollbars)
-       {
-               if(xscroll) xscroll->update_length(w_needed, get_xscroll(), w_visible, flush);
-               if(yscroll) yscroll->update_length(h_needed, get_yscroll(), h_visible, flush);
+       if( edl )
+               get_scrollbars(edl);
+       if( use_scrollbars ) {
+               if( xscroll ) xscroll->update_length(w_needed, get_xscroll(), w_visible, flush);
+               if( yscroll ) yscroll->update_length(h_needed, get_yscroll(), h_visible, flush);
        }
 }
 
-void Canvas::get_zooms(EDL *edl,
-       int single_channel,
-       float &zoom_x,
-       float &zoom_y,
-       float &conformed_w,
-       float &conformed_h)
+float Canvas::get_auto_zoom(EDL *edl)
 {
-       edl->calculate_conformed_dimensions(single_channel,
-               conformed_w,
-               conformed_h);
+       float conformed_w, conformed_h;
+       edl->calculate_conformed_dimensions(0, conformed_w, conformed_h);
+       BC_WindowBase *window = get_canvas();
+       int window_w = window ? window->get_w() : w;
+       int window_h = window ? window->get_h() : h;
+       float zoom_x = window_w / conformed_w;
+       float zoom_y = window_h / conformed_h;
+       return zoom_x < zoom_y ? zoom_x : zoom_y;
+}
+void Canvas::zoom_auto()
+{
+       use_scrollbars = 0;
+}
 
-       if(use_scrollbars)
-       {
-               zoom_x = get_zoom() *
-                       conformed_w /
-                       edl->session->output_w;
-               zoom_y = get_zoom() *
-                       conformed_h /
-                       edl->session->output_h;
-       }
-       else
-       {
-               int out_w, out_h;
-               int canvas_w = get_canvas()->get_w();
-               int canvas_h = get_canvas()->get_h();
+void Canvas::get_zooms(EDL *edl, int single_channel,
+               float &zoom_x, float &zoom_y,
+               float &conformed_w, float &conformed_h)
+{
+       edl->calculate_conformed_dimensions(single_channel,
+                       conformed_w, conformed_h);
 
-               out_w = canvas_w;
-               out_h = canvas_h;
+       float zoom = get_zoom();
+       if( !use_scrollbars || !zoom ) zoom = get_auto_zoom(edl);
+       zoom_x = zoom * conformed_w / get_output_w(edl);
+       zoom_y = zoom * conformed_h / get_output_h(edl);
+}
 
-               if((float)out_w / out_h > conformed_w / conformed_h)
-               {
-                       out_w = (int)((float)out_h * conformed_w / conformed_h + 0.5);
-               }
-               else
-               {
-                       out_h = (int)((float)out_w / (conformed_w / conformed_h) + 0.5);
-               }
+void Canvas::set_zoom(EDL *edl, float zoom)
+{
+       BC_WindowBase *window = get_canvas();
+       int cw = window ? window->get_w() : w;
+       int ch = window ? window->get_h() : h;
+       float cx = 0.5f * cw, cy = 0.5f * ch;
+       set_zoom(edl, zoom, cx, cy);
+}
 
-               zoom_x = (float)out_w / edl->session->output_w;
-               zoom_y = (float)out_h / edl->session->output_h;
-//printf("get zooms 2 %d %d %f %f\n", canvas_w, canvas_h, conformed_w, conformed_h);
+void Canvas::set_zoom(EDL *edl, float zoom, float cx, float cy)
+{
+       float output_x = cx, output_y = cy;
+       canvas_to_output(edl, 0, output_x, output_y);
+       update_zoom(0, 0, zoom);
+       float zoom_x, zoom_y, conformed_w, conformed_h;
+       get_zooms(edl, 0, zoom_x, zoom_y, conformed_w, conformed_h);
+       if( zoom ) {
+               output_x -= cx / zoom_x;
+               output_y -= cy / zoom_y;
+       }
+       else {
+               output_x = get_x_offset(edl, 0, zoom_x, conformed_w, conformed_h);
+               output_y = get_y_offset(edl, 0, zoom_y, conformed_w, conformed_h);
        }
+       update_zoom(output_x, output_y, zoom);
 }
 
+
 // Convert a coordinate on the canvas to a coordinate on the output
 void Canvas::canvas_to_output(EDL *edl, int single_channel, float &x, float &y)
 {
@@ -346,245 +289,150 @@ void Canvas::get_transfers(EDL *edl,
 //printf("Canvas::get_transfers %d canvas_w=%d canvas_h=%d\n", 
 // __LINE__,  canvas_w, canvas_h);
 // automatic canvas size detection
-       if(canvas_w < 0) canvas_w = get_canvas()->get_w();
-       if(canvas_h < 0) canvas_h = get_canvas()->get_h();
-
-// Canvas is zoomed to a portion of the output frame
-       if(use_scrollbars)
-       {
-               float in_x1, in_y1, in_x2, in_y2;
-               float out_x1, out_y1, out_x2, out_y2;
-               float zoom_x, zoom_y, conformed_w, conformed_h;
-
-               get_zooms(edl, 0, zoom_x, zoom_y, conformed_w, conformed_h);
-               out_x1 = 0;
-               out_y1 = 0;
-               out_x2 = canvas_w;
-               out_y2 = canvas_h;
-               in_x1 = 0;
-               in_y1 = 0;
-               in_x2 = canvas_w;
-               in_y2 = canvas_h;
-
-               canvas_to_output(edl, 0, in_x1, in_y1);
-               canvas_to_output(edl, 0, in_x2, in_y2);
-
-//printf("Canvas::get_transfers 1 %.0f %.0f %.0f %.0f -> %.0f %.0f %.0f %.0f\n",
-//in_x1, in_y1, in_x2, in_y2, out_x1, out_y1, out_x2, out_y2);
+       if( canvas_w < 0 ) canvas_w = get_canvas()->get_w();
+       if( canvas_h < 0 ) canvas_h = get_canvas()->get_h();
 
-               if(in_x1 < 0)
-               {
-                       out_x1 += -in_x1 * zoom_x;
-                       in_x1 = 0;
-               }
-
-               if(in_y1 < 0)
-               {
-                       out_y1 += -in_y1 * zoom_y;
-                       in_y1 = 0;
-               }
+       float in_x1, in_y1, in_x2, in_y2;
+       float out_x1, out_y1, out_x2, out_y2;
+       float zoom_x, zoom_y, conformed_w, conformed_h;
 
-               int output_w = get_output_w(edl);
-               int output_h = get_output_h(edl);
+       get_zooms(edl, 0, zoom_x, zoom_y, conformed_w, conformed_h);
+       out_x1 = 0;  out_x2 = canvas_w;
+       out_y1 = 0;  out_y2 = canvas_h;
+       in_x1 = 0;   in_x2  = canvas_w;
+       in_y1 = 0;   in_y2  = canvas_h;
+       canvas_to_output(edl, 0, in_x1, in_y1);
+       canvas_to_output(edl, 0, in_x2, in_y2);
 
-               if(in_x2 > output_w)
-               {
-                       out_x2 -= (in_x2 - output_w) * zoom_x;
-                       in_x2 = output_w;
-               }
+       if( in_x1 < 0 ) {
+               out_x1 += -in_x1 * zoom_x;
+               in_x1 = 0;
+       }
 
-               if(in_y2 > output_h)
-               {
-                       out_y2 -= (in_y2 - output_h) * zoom_y;
-                       in_y2 = output_h;
-               }
-// printf("Canvas::get_transfers 2 %.0f %.0f %.0f %.0f -> %.0f %.0f %.0f %.0f\n",
-//                     in_x1, in_y1, in_x2, in_y2, out_x1, out_y1, out_x2, out_y2);
-
-               output_x1 = in_x1;
-               output_y1 = in_y1;
-               output_x2 = in_x2;
-               output_y2 = in_y2;
-               canvas_x1 = out_x1;
-               canvas_y1 = out_y1;
-               canvas_x2 = out_x2;
-               canvas_y2 = out_y2;
-
-// Center on canvas
-//             if(!scrollbars_exist())
-//             {
-//                     out_x = canvas_w / 2 - out_w / 2;
-//                     out_y = canvas_h / 2 - out_h / 2;
-//             }
+       if( in_y1 < 0 ) {
+               out_y1 += -in_y1 * zoom_y;
+               in_y1 = 0;
+       }
 
+       int output_w = get_output_w(edl);
+       int output_h = get_output_h(edl);
+       if( in_x2 > output_w ) {
+               out_x2 -= (in_x2 - output_w) * zoom_x;
+               in_x2 = output_w;
        }
-       else
-// The output frame is normalized to the canvas
-       {
-// Default canvas coords fill the entire canvas
-               canvas_x1 = 0;
-               canvas_y1 = 0;
-               canvas_x2 = canvas_w;
-               canvas_y2 = canvas_h;
-
-               if(edl)
-               {
-// Use EDL aspect ratio to shrink one of the canvas dimensions
-                       float out_w = canvas_x2 - canvas_x1;
-                       float out_h = canvas_y2 - canvas_y1;
-                       if(out_w / out_h > edl->get_aspect_ratio())
-                       {
-                               out_w = (int)(out_h * edl->get_aspect_ratio() + 0.5);
-                               canvas_x1 = canvas_w / 2 - out_w / 2;
-                       }
-                       else
-                       {
-                               out_h = (int)(out_w / edl->get_aspect_ratio() + 0.5);
-                               canvas_y1 = canvas_h / 2 - out_h / 2;
-// printf("Canvas::get_transfers %d canvas_h=%d out_h=%f canvas_y1=%f\n",
-// __LINE__,
-// canvas_h,
-// out_h,
-// canvas_y1);
-                       }
-                       canvas_x2 = canvas_x1 + out_w;
-                       canvas_y2 = canvas_y1 + out_h;
-
-// Get output frame coords from EDL
-                       output_x1 = 0;
-                       output_y1 = 0;
-                       output_x2 = get_output_w(edl);
-                       output_y2 = get_output_h(edl);
-               }
-               else
-// No EDL to get aspect ratio or output frame coords from
-               {
-                       output_x1 = 0;
-                       output_y1 = 0;
-                       output_x2 = this->output_w;
-                       output_y2 = this->output_h;
-               }
+
+       if( in_y2 > output_h ) {
+               out_y2 -= (in_y2 - output_h) * zoom_y;
+               in_y2 = output_h;
        }
 
+       output_x1 = in_x1;   output_x2 = in_x2;
+       output_y1 = in_y1;   output_y2 = in_y2;
+       canvas_x1 = out_x1;  canvas_x2 = out_x2;
+       canvas_y1 = out_y1;  canvas_y2 = out_y2;
+
 // Clamp to minimum value
-       output_x1 = MAX(0, output_x1);
-       output_y1 = MAX(0, output_y1);
-       output_x2 = MAX(output_x1, output_x2);
-       output_y2 = MAX(output_y1, output_y2);
-       canvas_x1 = MAX(0, canvas_x1);
-       canvas_y1 = MAX(0, canvas_y1);
-       canvas_x2 = MAX(canvas_x1, canvas_x2);
-       canvas_y2 = MAX(canvas_y1, canvas_y2);
-// printf("Canvas::get_transfers %d %f,%f %f,%f -> %f,%f %f,%f\n",
-// __LINE__,
-// output_x1,
-// output_y1,
-// output_x2,
-// output_y2,
-// canvas_x1,
-// canvas_y1,
-// canvas_x2,
-// canvas_y2);
+       if( output_x1 < 0 ) output_x1 = 0;
+       if( output_x2 < output_x1 ) output_x2 = output_x1;
+       if( canvas_x1 < 0 ) canvas_x1 = 0;
+       if( canvas_x2 < canvas_x1 ) canvas_x2 = canvas_x1;
+// printf("Canvas::get_transfers %d %f,%f %f,%f -> %f,%f %f,%f\n", __LINE__,
+//  output_x1, output_y1, output_x2, output_y2,
+//  canvas_x1, canvas_y1, canvas_x2, canvas_y2);
 }
 
 int Canvas::scrollbars_exist()
 {
-       return(use_scrollbars && (xscroll || yscroll));
+       return use_scrollbars && (xscroll || yscroll);
 }
 
 int Canvas::get_output_w(EDL *edl)
 {
-       return !edl ? 0 : edl->session->output_w;
+       return edl->session->output_w;
 }
 
 int Canvas::get_output_h(EDL *edl)
 {
-       return !edl ? 0 : edl->session->output_h;
+       return edl->session->output_h;
 }
 
 
-
-void Canvas::get_scrollbars(EDL *edl,
-       int &canvas_x,
-       int &canvas_y,
-       int &canvas_w,
-       int &canvas_h)
+int Canvas::get_scrollbars(EDL *edl)
 {
-       int need_xscroll = 0;
-       int need_yscroll = 0;
-//     int done = 0;
-       float zoom_x, zoom_y, conformed_w, conformed_h;
+       int ret = 0;
+       BC_WindowBase *window = get_canvas();
+       if( !window ) use_scrollbars = 0;
+       int canvas_w = w, canvas_h = h;
 
-       if(edl)
-       {
-               w_needed = edl->session->output_w;
-               h_needed = edl->session->output_h;
-               w_visible = w_needed;
-               h_visible = h_needed;
-       }
-//printf("Canvas::get_scrollbars 1 %d %d\n", get_xscroll(), get_yscroll());
+       w_needed = w_visible = edl ? edl->session->output_w : view_w;
+       h_needed = h_visible = edl ? edl->session->output_h : view_h;
 
-       if( use_scrollbars ) {
-               w_needed = edl->session->output_w;
-               h_needed = edl->session->output_h;
+       int need_xscroll = 0, need_yscroll = 0;
+       if( edl && use_scrollbars ) {
+               float zoom_x, zoom_y, conformed_w, conformed_h;
                get_zooms(edl, 0, zoom_x, zoom_y, conformed_w, conformed_h);
-//printf("Canvas::get_scrollbars 2 %d %d\n", get_xscroll(), get_yscroll());
-
-               w_visible = (int)(canvas_w / zoom_x);
-               h_visible = (int)(canvas_h / zoom_y);
-               if( w_needed > w_visible ) {
-                       need_xscroll = 1;
+               w_visible = canvas_w / zoom_x;
+               h_visible = canvas_h / zoom_y;
+               float output_x = 0, output_y = 0;
+               output_to_canvas(edl, 0, output_x, output_y);
+               if( output_x < 0 ) need_xscroll = 1;
+               if( output_y < 0 ) need_yscroll = 1;
+               output_x = w_needed, output_y = h_needed;
+               output_to_canvas(edl, 0, output_x, output_y);
+               if( output_x > canvas_w ) need_xscroll = 1;
+               if( output_y > canvas_h ) need_yscroll = 1;
+               if( need_xscroll ) {
                        canvas_h -= BC_ScrollBar::get_span(SCROLL_HORIZ);
+                       h_visible = canvas_h / zoom_y;
                }
-
-               if( h_needed > h_visible ) {
-                       need_yscroll = 1;
+               if( need_yscroll ) {
                        canvas_w -= BC_ScrollBar::get_span(SCROLL_VERT);
+                       w_visible = canvas_w / zoom_x;
                }
-//printf("Canvas::get_scrollbars %d %d %d %d %d %d\n", canvas_w, canvas_h, w_needed, h_needed, w_visible, h_visible);
-//printf("Canvas::get_scrollbars 3 %d %d\n", get_xscroll(), get_yscroll());
-
-               w_visible = (int)(canvas_w / zoom_x);
-               h_visible = (int)(canvas_h / zoom_y);
+               view_w = canvas_w;
+               view_h = canvas_h;
        }
 
        if( need_xscroll ) {
                if( !xscroll ) {
-                       xscroll = new CanvasXScroll(edl, this, canvas_x, canvas_y + canvas_h,
-                                       w_needed, get_xscroll(), w_visible, canvas_w);
+                       xscroll = new CanvasXScroll(edl, this, view_x, view_y + view_h,
+                                       w_needed, get_xscroll(), w_visible, view_w);
                        subwindow->add_subwindow(xscroll);
                        xscroll->show_window(0);
                }
                else
-                       xscroll->reposition_window(canvas_x, canvas_y + canvas_h, canvas_w);
+                       xscroll->reposition_window(view_x, view_y + view_h, view_w);
 
                if( xscroll->get_length() != w_needed ||
                    xscroll->get_handlelength() != w_visible )
                        xscroll->update_length(w_needed, get_xscroll(), w_visible, 0);
+               ret = 1;
        }
        else if( xscroll ) {
                delete xscroll;  xscroll = 0;
+               ret = 1;
        }
-//printf("Canvas::get_scrollbars 4 %d %d\n", get_xscroll(), get_yscroll());
 
        if( need_yscroll ) {
                if( !yscroll ) {
-                       yscroll = new CanvasYScroll(edl, this, canvas_x + canvas_w, canvas_y,
-                                       h_needed, get_yscroll(), h_visible, canvas_h);
+                       yscroll = new CanvasYScroll(edl, this, view_x + view_w, view_y,
+                                       h_needed, get_yscroll(), h_visible, view_h);
                        subwindow->add_subwindow(yscroll);
                        yscroll->show_window(0);
                }
                else
-                       yscroll->reposition_window(canvas_x + canvas_w, canvas_y, canvas_h);
+                       yscroll->reposition_window(view_x + view_w, view_y, view_h);
 
                if( yscroll->get_length() != edl->session->output_h ||
                    yscroll->get_handlelength() != h_visible )
                        yscroll->update_length(h_needed, get_yscroll(), h_visible, 0);
+               ret = 1;
        }
        else if( yscroll ) {
                delete yscroll;  yscroll = 0;
+               ret = 1;
        }
-//printf("Canvas::get_scrollbars 5 %d %d\n", get_xscroll(), get_yscroll());
+       return ret;
 }
 
 
@@ -592,15 +440,12 @@ void Canvas::update_geometry(EDL *edl, int x, int y, int w, int h)
 {
        int redraw = 0;
        if( this->x != x || this->y != y ||
-           this->w != w || this->h != h ) redraw = 1;
-       if( !redraw ) {
-               int vx = x, vy = y, vw = w, vh = h;
-               get_scrollbars(edl, vx, vy, vw, vh);
-               if( vx != view_x || vy != view_y ||
-                   vw != view_w || vh != view_h ) redraw = 1;
-       }
-       if( !redraw ) return;
-       reposition_window(edl, x, y, w, y);
+           this->w != w || this->h != h )
+               redraw = 1;
+       if( !redraw )
+               redraw = get_scrollbars(edl);
+       if( redraw )
+               reposition_window(edl, x, y, w, h);
 }
 
 void Canvas::reposition_window(EDL *edl, int x, int y, int w, int h)
@@ -608,22 +453,37 @@ void Canvas::reposition_window(EDL *edl, int x, int y, int w, int h)
        this->x = view_x = x;  this->y = view_y = y;
        this->w = view_w = w;  this->h = view_h = h;
 //printf("Canvas::reposition_window 1\n");
-       get_scrollbars(edl, view_x, view_y, view_w, view_h);
+       get_scrollbars(edl);
 //printf("Canvas::reposition_window %d %d %d %d\n", view_x, view_y, view_w, view_h);
-       if(canvas_subwindow)
-       {
+       if( canvas_subwindow ) {
                canvas_subwindow->reposition_window(view_x, view_y, view_w, view_h);
 
 // Need to clear out the garbage in the back
-               if(canvas_subwindow->get_video_on())
-               {
-                       canvas_subwindow->set_color(BLACK);
-                       canvas_subwindow->draw_box(0, 0,
-                               get_canvas()->get_w(), get_canvas()->get_h());
-                       canvas_subwindow->flash(0);
-               }
+               if( canvas_subwindow->get_video_on() )
+                       clear_borders(edl);
        }
-       draw_refresh(0);
+       refresh(0);
+}
+
+// must hold window lock
+int Canvas::refresh(int flush)
+{
+       BC_WindowBase *window = get_canvas();
+       if( !window ) return 0;
+// relock in lock order to prevent deadlock
+       window->unlock_window();
+       lock_canvas("Canvas::refresh");
+       draw_refresh(flush);
+       canvas_lock->unlock();
+       return 1;
+}
+// must not hold locks
+int Canvas::redraw(int flush)
+{
+       lock_canvas("Canvas::redraw");
+       draw_refresh(flush);
+       unlock_canvas();
+       return 1;
 }
 
 void Canvas::set_cursor(int cursor)
@@ -649,11 +509,9 @@ int Canvas::get_buttonpress()
 
 void Canvas::create_objects(EDL *edl)
 {
-       view_x = x;
-       view_y = y;
-       view_w = w;
-       view_h = h;
-       get_scrollbars(edl, view_x, view_y, view_w, view_h);
+       view_x = x;  view_y = y;
+       view_w = w;  view_h = h;
+       get_scrollbars(edl);
 
        subwindow->unlock_window();
        create_canvas();
@@ -713,60 +571,67 @@ void Canvas::stop_video()
        }
 }
 
+int Canvas::set_fullscreen(int on, int unlock)
+{
+       int ret = 0;
+       BC_WindowBase *window = get_canvas();
+       if( unlock )
+               window->unlock_window();
+       if( on && !get_fullscreen() ) {
+               start_fullscreen();
+               ret = 1;
+       }
+       if( !on && get_fullscreen() ) {
+               stop_fullscreen();
+               ret = 1;
+       }
+       if( unlock )
+               window->lock_window("Canvas::set_fullscreen");
+       return ret;
+}
 
 void Canvas::start_fullscreen()
 {
-       set_fullscreen(1);
+       is_fullscreen = 1;
        create_canvas();
 }
 
 void Canvas::stop_fullscreen()
 {
-       set_fullscreen(0);
+       is_fullscreen = 0;
        create_canvas();
 }
 
 void Canvas::create_canvas()
 {
+       canvas_lock->lock("Canvas::create_canvas");
        int video_on = 0;
-       lock_canvas("Canvas::create_canvas");
-
-
-       if(!get_fullscreen())
+       BC_WindowBase *wdw = 0;
+       if( !get_fullscreen() ) {
 // Enter windowed
-       {
-               if(canvas_fullscreen)
-               {
+               if( canvas_fullscreen ) {
+                       canvas_fullscreen->lock_window("Canvas::create_canvas 1");
                        video_on = canvas_fullscreen->get_video_on();
-                       canvas_fullscreen->stop_video();
-                       canvas_fullscreen->lock_window("Canvas::create_canvas 2");
+                       if( video_on ) canvas_fullscreen->stop_video();
                        canvas_fullscreen->hide_window();
                        canvas_fullscreen->unlock_window();
                }
-
-               if(!canvas_auxwindow && !canvas_subwindow)
-               {
+               if( !canvas_auxwindow && !canvas_subwindow ) {
                        subwindow->add_subwindow(canvas_subwindow = new CanvasOutput(this,
-                               view_x,
-                               view_y,
-                               view_w,
-                               view_h));
+                               view_x, view_y, view_w, view_h));
                }
+               wdw = get_canvas();
+               wdw->lock_window("Canvas::create_canvas 2");
        }
-       else
+       else {
 // Enter fullscreen
-       {
-               BC_WindowBase *wdw = canvas_auxwindow ?
-                       canvas_auxwindow : canvas_subwindow;
-               if(wdw)
-               {
-                       video_on = wdw->get_video_on();
-                       wdw->stop_video();
-               }
-
+               wdw = canvas_auxwindow ? canvas_auxwindow : canvas_subwindow;
+               wdw->lock_window("Canvas::create_canvas 3");
+               video_on = wdw->get_video_on();
+               if( video_on ) wdw->stop_video();
                int x, y, w, h;
                wdw->get_fullscreen_geometry(x, y, w, h);
-
+               wdw->unlock_window();
                if( canvas_fullscreen ) {
                        if( x != canvas_fullscreen->get_x() ||
                            y != canvas_fullscreen->get_y() ||
@@ -778,29 +643,24 @@ void Canvas::create_canvas()
                }
                if( !canvas_fullscreen )
                        canvas_fullscreen = new CanvasFullScreen(this, w, h);
-               canvas_fullscreen->show_window();
-               canvas_fullscreen->sync_display();
-               canvas_fullscreen->reposition_window(x, y);
-       }
-
-       if( !video_on ) {
-               get_canvas()->lock_window("Canvas::create_canvas 1");
-               draw_refresh();
-               get_canvas()->unlock_window();
+               wdw = canvas_fullscreen;
+               wdw->lock_window("Canvas::create_canvas 4");
+               wdw->show_window();
+               wdw->sync_display();
+               wdw->reposition_window(x, y);
        }
 
        if( video_on )
-               get_canvas()->start_video();
-
-       get_canvas()->lock_window("Canvas::create_canvas 2");
-       get_canvas()->focus();
-       get_canvas()->unlock_window();
+               wdw->start_video();
+       else
+               draw_refresh(1);
 
-       unlock_canvas();
+       wdw->focus();
+       wdw->unlock_window();
+       canvas_lock->unlock();
 }
 
 
-
 int Canvas::cursor_leave_event_base(BC_WindowBase *caller)
 {
        int result = 0;
@@ -832,24 +692,19 @@ int Canvas::button_press_event_base(BC_WindowBase *caller)
 int Canvas::keypress_event(BC_WindowBase *caller)
 {
        int key = caller->get_keypress();
+       int on = -1;
        switch( key ) {
        case 'f':
-               caller->unlock_window();
-               if(get_fullscreen())
-                       stop_fullscreen();
-               else
-                       start_fullscreen();
-               caller->lock_window("Canvas::keypress_event 1");
+               on = get_fullscreen() ? 0 : 1;
                break;
        case ESC:
-               caller->unlock_window();
-               if(get_fullscreen())
-                       stop_fullscreen();
-               caller->lock_window("Canvas::keypress_event 2");
+               on = 0;
                break;
        default:
                return 0;
        }
+       if( on >= 0 )
+               set_fullscreen(on);
        return 1;
 }
 
@@ -889,12 +744,9 @@ void Canvas::update_refresh(VideoDevice *device, VFrame *output_frame)
        }
 
        if( use_opengl ) {
-               get_canvas()->unlock_window();
                unlock_canvas();
-
                mwindow->playback_3d->copy_from(this, refresh_frame, output_frame, 0);
-               lock_canvas(" Canvas::output_refresh");
-               get_canvas()->lock_window(" Canvas::output_refresh");
+               lock_canvas("Canvas::update_refresh");
        }
        else
                refresh_frame->transfer_from(output_frame, -1);
@@ -902,20 +754,67 @@ void Canvas::update_refresh(VideoDevice *device, VFrame *output_frame)
 
 void Canvas::clear(int flash)
 {
-       BC_WindowBase *cwdw = get_canvas();
-       if( !cwdw )  return;
-       cwdw->set_bg_color(BLACK);
-       cwdw->clear_box(0,0, cwdw->get_w(), cwdw->get_h());
-       if( flash ) cwdw->flash();
+       BC_WindowBase *window = get_canvas();
+       if( !window ) return;
+       window->set_bg_color(get_clear_color());
+       window->clear_box(0,0, window->get_w(), window->get_h());
+       if( flash ) window->flash();
 }
 
+void Canvas::clear_borders(EDL *edl)
+{
+       BC_WindowBase *window = get_canvas();
+       if( !window ) return;
+       int window_w = window->get_w();
+       int window_h = window->get_h();
+       int color = get_clear_color();
+       window->set_color(color);
+
+       if( !edl ) {
+               window->draw_box(0, 0, window_w, window_h);
+               window->flash(0);
+               return;
+       }
+
+       float output_x1,output_y1, output_x2,output_y2;
+       float canvas_x1,canvas_y1, canvas_x2,canvas_y2;
+       get_transfers(edl,
+               output_x1, output_y1, output_x2, output_y2,
+               canvas_x1, canvas_y1, canvas_x2, canvas_y2);
+
+       if( canvas_y1 > 0 ) {
+               window->draw_box(0, 0, window_w, canvas_y1);
+               window->flash(0, 0, window_w, canvas_y1);
+       }
+
+       if( canvas_y2 < window_h ) {
+               window->draw_box(0, canvas_y2, window_w, window_h-canvas_y2);
+               window->flash(0, canvas_y2, window_w, window_h-canvas_y2);
+       }
+
+       if( canvas_x1 > 0 ) {
+               window->draw_box(0, canvas_y1, canvas_x1, canvas_y2-canvas_y1);
+               window->flash(0, canvas_y1, canvas_x1, canvas_y2-canvas_y1);
+       }
+
+       if( canvas_x2 < window_w ) {
+               window->draw_box(canvas_x2, canvas_y1,
+                       window_w-canvas_x2, canvas_y2-canvas_y1);
+               window->flash(canvas_x2, canvas_y1,
+                       window_w-canvas_x2, canvas_y2-canvas_y1);
+       }
+}
+
+int Canvas::get_clear_color()
+{
+       BC_WindowBase *cwdw = get_canvas();
+       if( !cwdw ) return 0;
+       return cwdw->get_bg_color();
+}
 
 CanvasOutput::CanvasOutput(Canvas *canvas,
-    int x,
-    int y,
-    int w,
-    int h)
- : BC_SubWindow(x, y, w, h, BLACK)
+    int x, int y, int w, int h)
+ : BC_SubWindow(x, y, w, h, canvas->get_clear_color())
 {
        this->canvas = canvas;
 }
@@ -957,7 +856,7 @@ int CanvasOutput::keypress_event()
 
 
 CanvasFullScreen::CanvasFullScreen(Canvas *canvas, int w, int h)
- : BC_FullScreen(canvas->subwindow, w, h, BLACK, 0, 0, 0)
+ : BC_FullScreen(canvas->subwindow, w, h, canvas->get_clear_color(), 0, 0, 0)
 {
        this->canvas = canvas;
 }
@@ -980,10 +879,9 @@ CanvasXScroll::~CanvasXScroll()
 
 int CanvasXScroll::handle_event()
 {
-//printf("CanvasXScroll::handle_event %d %d %d\n", get_length(), get_value(), get_handlelength());
-       canvas->update_zoom(get_value(), canvas->get_yscroll(), canvas->get_zoom());
-       canvas->draw_refresh();
-       return 1;
+       canvas->update_zoom(get_value(), canvas->get_yscroll(),
+                       canvas->get_zoom());
+       return canvas->refresh(1);
 }
 
 
@@ -1000,10 +898,9 @@ CanvasYScroll::~CanvasYScroll()
 
 int CanvasYScroll::handle_event()
 {
-//printf("CanvasYScroll::handle_event %d %d\n", get_value(), get_length());
-       canvas->update_zoom(canvas->get_xscroll(), get_value(), canvas->get_zoom());
-       canvas->draw_refresh();
-       return 1;
+       canvas->update_zoom(canvas->get_xscroll(), get_value(),
+                       canvas->get_zoom());
+       return canvas->refresh(1);
 }
 
 
@@ -1034,9 +931,7 @@ int CanvasSubWindowItem::handle_event()
 {
 // It isn't a problem to delete the canvas from in here because the event
 // dispatcher is the canvas subwindow.
-       canvas->subwindow->unlock_window();
-       canvas->stop_fullscreen();
-       canvas->subwindow->lock_window("CanvasSubWindowItem::handle_event");
+       canvas->set_fullscreen(0);
        return 1;
 }
 
@@ -1133,7 +1028,6 @@ int CanvasPopupSize::handle_event()
 }
 
 
-
 CanvasPopupResetCamera::CanvasPopupResetCamera(Canvas *canvas)
  : BC_MenuItem(_("Reset camera"), _("F11"), KEY_F11)
 {
@@ -1210,18 +1104,10 @@ int CanvasToggleControls::handle_event()
 
 char* CanvasToggleControls::calculate_text(int cwindow_controls)
 {
-       if(!cwindow_controls)
-               return _("Show controls");
-       else
-               return _("Hide controls");
+       return !cwindow_controls ? _("Show controls") : _("Hide controls");
 }
 
 
-
-
-
-
-
 CanvasFullScreenItem::CanvasFullScreenItem(Canvas *canvas)
  : BC_MenuItem(_("Fullscreen"), "f", 'f')
 {
@@ -1229,20 +1115,11 @@ CanvasFullScreenItem::CanvasFullScreenItem(Canvas *canvas)
 }
 int CanvasFullScreenItem::handle_event()
 {
-       canvas->subwindow->unlock_window();
-       canvas->start_fullscreen();
-       canvas->subwindow->lock_window("CanvasFullScreenItem::handle_event");
+       canvas->set_fullscreen(1);
        return 1;
 }
 
 
-
-
-
-
-
-
-
 CanvasPopupRemoveSource::CanvasPopupRemoveSource(Canvas *canvas)
  : BC_MenuItem(_("Close source"))
 {