rest of Termux related mods from Andrew
[goodguy/cinelerra.git] / cinelerra-5.1 / guicast / bcwindowbase.C
index ad25943b3ae97e540d83c0dc06e33248d46f37e4..c57cd6203c8f6569a1b24b6feb7c2cfbb7d9f6a9 100644 (file)
@@ -57,7 +57,9 @@
 #include <typeinfo>
 
 #include <X11/extensions/Xinerama.h>
+#ifdef HAVE_XV
 #include <X11/extensions/Xvlib.h>
+#endif
 #include <X11/extensions/shape.h>
 #include <X11/XF86keysym.h>
 #include <X11/Sunkeysym.h>
@@ -70,7 +72,7 @@ BC_ResizeCall::BC_ResizeCall(int w, int h)
 
 
 int BC_WindowBase::shm_completion_event = -1;
-BC_Resources BC_WindowBase::resources;
+BC_Resources *BC_WindowBase::resources = 0;
 Window XGroupLeader = 0;
 
 Mutex BC_KeyboardHandlerLock::keyboard_listener_mutex("keyboard_listener",0);
@@ -205,9 +207,10 @@ BC_WindowBase::~BC_WindowBase()
                        XFree(xinerama_info);
                xinerama_screens = 0;
                xinerama_info = 0;
+#ifdef HAVE_XV
                if( xvideo_port_id >= 0 )
                        XvUngrabPort(display, xvideo_port_id, CurrentTime);
-
+#endif
                unlock_window();
 // Must be last reference to display.
 // _XftDisplayInfo needs a lock.
@@ -221,6 +224,7 @@ BC_WindowBase::~BC_WindowBase()
        }
 
        resize_history.remove_all_objects();
+       delete grab_lock;
 
 #ifndef SINGLE_THREAD
        common_events.remove_all_objects();
@@ -250,6 +254,7 @@ int BC_WindowBase::initialize()
        display_lock_owner = 0;
        test_keypress = 0;
        keys_return[0] = 0;
+       context_help_keyword[0] = 0;
        is_deleting = 0;
        window_lock = 0;
        resend_event_window = 0;
@@ -343,6 +348,7 @@ int BC_WindowBase::initialize()
        event_condition = new Condition(0, "BC_WindowBase::event_condition");
        init_lock = new Condition(0, "BC_WindowBase::init_lock");
 #endif
+       grab_lock = new Mutex("BC_WindowBase::grab_lock");
 
        cursor_timer = new Timer;
        event_thread = 0;
@@ -473,7 +479,7 @@ int BC_WindowBase::create_window(BC_WindowBase *parent_window, const char *title
 // This must be done before fonts to know if antialiasing is available.
                init_colors();
 // get the resources
-               if(resources.use_shm < 0) resources.initialize_display(this);
+               if(resources->use_shm < 0) resources->initialize_display(this);
                x_correction = BC_DisplayInfo::get_left_border();
                y_correction = BC_DisplayInfo::get_top_border();
 
@@ -486,7 +492,7 @@ int BC_WindowBase::create_window(BC_WindowBase *parent_window, const char *title
                if(this->y < 0) this->y = 0;
 
                if(this->bg_color == -1)
-                       this->bg_color = resources.get_bg_color();
+                       this->bg_color = resources->get_bg_color();
 
 // printf("bcwindowbase 1 %s\n", title);
 // if(window_type == MAIN_WINDOW) sleep(1);
@@ -576,7 +582,7 @@ int BC_WindowBase::create_window(BC_WindowBase *parent_window, const char *title
                        KeyPressMask | KeyReleaseMask;
 
                if(this->bg_color == -1)
-                       this->bg_color = resources.get_bg_color();
+                       this->bg_color = resources->get_bg_color();
                attr.background_pixel = top_level->get_color(bg_color);
                attr.colormap = top_level->cmap;
                if(top_level->is_hourglass)
@@ -624,7 +630,7 @@ int BC_WindowBase::create_window(BC_WindowBase *parent_window, const char *title
                }
 
                if(!hidden) show_window();
-
+               init_glyphs();
        }
 
        draw_background(0, 0, this->w, this->h);
@@ -869,8 +875,11 @@ int BC_WindowBase::keysym_lookup(XEvent *event)
        wkey_string_length = 0;
 
        if( input_context ) {
+               wchar_t wkey[4];
                wkey_string_length = XwcLookupString(input_context,
-                       (XKeyEvent*)event, wkey_string, 4, &keysym, 0);
+                       (XKeyEvent*)event, wkey, 4, &keysym, 0);
+               for( int i=0; i<wkey_string_length; ++i )
+                       wkey_string[i] = wkey[i];
 //printf("keysym_lookup 1 %d %d %lx %x %x %x %x\n", wkey_string_length, keysym,
 //  wkey_string[0], wkey_string[1], wkey_string[2], wkey_string[3]);
 
@@ -953,10 +962,15 @@ if( debug && event->type != ClientMessage ) {
 }
 
        if( active_grab ) {
+               grab_lock->lock("BC_WindowBase::dispatch_event 3");
                unlock_window();
-               active_grab->lock_window("BC_WindowBase::dispatch_event 3");
-               result = active_grab->grab_event(event);
-               active_grab->unlock_window();
+               result = 0;
+               if( active_grab ) {
+                       active_grab->lock_window("BC_WindowBase::dispatch_event 3");
+                       result = active_grab->grab_event(event);
+                       active_grab->unlock_window();
+               }
+               grab_lock->unlock();
                if( result ) return result;
                lock_window("BC_WindowBase::dispatch_event 4");
        }
@@ -1016,8 +1030,8 @@ if( debug && event->type != ClientMessage ) {
 
 //printf("BC_WindowBase::dispatch_event %d %d\n", __LINE__, button_number);
                event_win = event->xany.window;
-               if (button_number < 6) {
-                       if(button_number < 4)
+               if( button_number < 6 ) {
+                       if( button_number < 4 )
                                button_down = 1;
                        button_pressed = event->xbutton.button;
                        button_time1 = button_time2;
@@ -1030,23 +1044,20 @@ if( debug && event->type != ClientMessage ) {
                        drag_x2 = cursor_x + get_resources()->drag_radius;
                        drag_y1 = cursor_y - get_resources()->drag_radius;
                        drag_y2 = cursor_y + get_resources()->drag_radius;
-
-                       if((long)(button_time3 - button_time1) < resources.double_click * 2)
-                       {
-                               triple_click = 1;
-                               button_time3 = button_time2 = button_time1 = 0;
-                       }
-                       if((long)(button_time3 - button_time2) < resources.double_click)
-                       {
-                               double_click = 1;
-//                             button_time3 = button_time2 = button_time1 = 0;
-                       }
-                       else
-                       {
-                               triple_click = 0;
-                               double_click = 0;
+                       if( button_number < 4 ) {
+                               if((long)(button_time3 - button_time1) < resources->double_click * 2) {
+                                       triple_click = 1;
+                                       button_time3 = button_time2 = button_time1 = 0;
+                               }
+                               if((long)(button_time3 - button_time2) < resources->double_click) {
+                                       double_click = 1;
+//                                     button_time3 = button_time2 = button_time1 = 0;
+                               }
+                               else {
+                                       triple_click = 0;
+                                       double_click = 0;
+                               }
                        }
-
                        dispatch_button_press();
                }
                break;
@@ -1261,35 +1272,8 @@ if( debug && event->type != ClientMessage ) {
                case XK_F10:            key_pressed = KEY_F10;  break;
                case XK_F11:            key_pressed = KEY_F11;  break;
                case XK_F12:            key_pressed = KEY_F12;  break;
-
+// activates remote
                case XK_Menu:           key_pressed = KPMENU;   break;  /* menu */
-// remote control
-// above       case XK_KP_Enter:       key_pressed = KPENTER;  break;  /* check */
-               case XF86XK_MenuKB:     key_pressed = KPMENU;   break;  /* menu */
-// intercepted case XF86XK_PowerDown: key_pressed = KPPOWER;   break;  /* Power */
-               case XF86XK_Launch1:    key_pressed = KPTV;     break;  /* TV */
-               case XF86XK_Launch2:    key_pressed = KPDVD;    break;  /* DVD */
-// intercepted case XF86XK_WWW:        key_pressed = KPWWEB;   break;  /* WEB */
-               case XF86XK_Launch3:    key_pressed = KPBOOK;   break;  /* book */
-               case XF86XK_Launch4:    key_pressed = KPHAND;   break;  /* hand */
-               case XF86XK_Reply:      key_pressed = KPTMR;    break;  /* timer */
-               case SunXK_Front:       key_pressed = KPMAXW;   break;  /* max */
-// above       case XK_Left:           key_pressed = LEFT;     break;  /* left */
-// above       case XK_Right:          key_pressed = RIGHT;    break;  /* right */
-// above       case XK_Down:           key_pressed = DOWN;     break;  /* down */
-// above       case XK_Up:             key_pressed = UP;       break;  /* up */
-// above       case XK_SPACE:          key_pressed = KPSPACE;  break;  /* ok */
-// intercepted case XF86XK_AudioRaiseVolume: key_pressed = KPVOLU;     break;  /* VOL + */
-// intercepted case XF86XK_AudioMute: key_pressed = KPMUTE;    break;  /* MUTE */
-// intercepted case XF86XK_AudioLowerVolume: key_pressed = KPVOLD;     break;  /* VOL - */
-               case XF86XK_ScrollUp:   key_pressed = KPCHUP;   break;  /* CH + */
-               case XF86XK_ScrollDown: key_pressed = KPCHDN;   break;  /* CH - */
-               case XF86XK_AudioRecord: key_pressed = KPRECD;  break;  /* ( o) red */
-               case XF86XK_Forward:    key_pressed = KPPLAY;   break;  /* ( >) */
-               case XK_Redo:           key_pressed = KPFWRD;   break;  /* (>>) */
-               case XF86XK_Back:       key_pressed = KPBACK;   break;  /* (<<) */
-               case XK_Cancel:         key_pressed = KPSTOP;   break;  /* ([]) */
-               case XK_Pause:          key_pressed = KPAUSE;   break;  /* ('') */
 
                default:
                        key_pressed = keysym & 0xff;
@@ -1747,6 +1731,90 @@ int BC_WindowBase::close_event()
        return 1;
 }
 
+// *** CONTEXT_HELP ***
+// This basic implementation serves solely for context help.
+// We are handling Alt/H only. Any subclass requiring more sophisticated
+// processing of keystrokes has to provide its own overloaded handler.
+int BC_WindowBase::keypress_event()
+{
+//     printf("BC_WindowBase::keypress_event: %d\n", get_keypress());
+       return context_help_check_and_show();
+}
+
+// The stuff up to END_CONTEXT_HELP serves solely for context help
+void BC_WindowBase::context_help_set_keyword(const char *keyword)
+{
+       if (keyword) strcpy(context_help_keyword, keyword);
+}
+
+const char *BC_WindowBase::context_help_get_keyword()
+{
+// If we have context_help_keyword defined here, return it.
+// Otherwise recursively track widget hierarchy up to top_level
+// and return the nearest found nonempty keyword.
+// If nothing found, the special keyword "TOC" is returned.
+       if (context_help_keyword[0] || this == top_level ||
+           this == parent_window || ! parent_window) {
+               if (! context_help_keyword[0] && this == top_level)
+                       context_help_set_keyword("TOC");
+               return context_help_keyword;
+       }
+       return parent_window->context_help_get_keyword();
+}
+
+void BC_WindowBase::context_help_show(const char *keyword)
+{
+       char cmd[BCTEXTLEN];
+
+       if (! keyword) return;
+
+       sprintf(cmd, "\"%s/doc/ContextManual.pl\" \"%s\"", getenv("CIN_DAT"),
+               keyword);
+//     printf("BC_WindowBase::context_help_show(%s):\n%s\n", keyword, cmd);
+
+// ContextManual.pl starts browser in background, so system() should not block
+       system(cmd);
+}
+
+void BC_WindowBase::context_help_show()
+{
+       context_help_show(context_help_get_keyword());
+}
+
+int BC_WindowBase::context_help_check_and_show(const char *keyword)
+{
+       if (! keyword)    return 0;
+       if (! keyword[0]) return 0;
+
+// We are handling Alt/H only
+       if (get_keypress() != 'h' || ! alt_down()) return 0;
+
+// Restrict cursor location to that widget keystroke occured in
+       if (! is_tooltip_event_win() || ! cursor_inside()) return 0;
+
+       context_help_show(keyword);
+       return 1;
+}
+
+int BC_WindowBase::context_help_check_and_show()
+{
+       const char *keyword;
+
+// We are handling Alt/H only
+       if (get_keypress() != 'h' || ! alt_down()) return 0;
+
+// Restrict cursor location, so any subwindow can define its own help keyword
+       if (! is_tooltip_event_win() || ! cursor_inside()) return 0;
+
+// If a widget has not defined its own help keyword, the parent can provide it
+       keyword = context_help_get_keyword();
+       if (! keyword[0]) return 0;
+
+       context_help_show(keyword);
+       return 1;
+}
+// *** END_CONTEXT_HELP ***
+
 int BC_WindowBase::dispatch_drag_start()
 {
        int result = 0;
@@ -2295,21 +2363,21 @@ int BC_WindowBase::init_gc()
 
 int BC_WindowBase::init_fonts()
 {
-       if( !(smallfont = XLoadQueryFont(display, _(resources.small_font))) )
-               if( !(smallfont = XLoadQueryFont(display, _(resources.small_font2))) )
+       if( !(smallfont = XLoadQueryFont(display, _(resources->small_font))) )
+               if( !(smallfont = XLoadQueryFont(display, _(resources->small_font2))) )
                        smallfont = XLoadQueryFont(display, "fixed");
-       if( !(mediumfont = XLoadQueryFont(display, _(resources.medium_font))) )
-               if( !(mediumfont = XLoadQueryFont(display, _(resources.medium_font2))) )
+       if( !(mediumfont = XLoadQueryFont(display, _(resources->medium_font))) )
+               if( !(mediumfont = XLoadQueryFont(display, _(resources->medium_font2))) )
                        mediumfont = XLoadQueryFont(display, "fixed");
-       if( !(largefont = XLoadQueryFont(display, _(resources.large_font))) )
-               if( !(largefont = XLoadQueryFont(display, _(resources.large_font2))) )
+       if( !(largefont = XLoadQueryFont(display, _(resources->large_font))) )
+               if( !(largefont = XLoadQueryFont(display, _(resources->large_font2))) )
                        largefont = XLoadQueryFont(display, "fixed");
-       if( !(bigfont = XLoadQueryFont(display, _(resources.big_font))) )
-               if( !(bigfont = XLoadQueryFont(display, _(resources.big_font2))) )
+       if( !(bigfont = XLoadQueryFont(display, _(resources->big_font))) )
+               if( !(bigfont = XLoadQueryFont(display, _(resources->big_font2))) )
                        bigfont = XLoadQueryFont(display, "fixed");
 
-       if((clockfont = XLoadQueryFont(display, _(resources.clock_font))) == NULL)
-               if((clockfont = XLoadQueryFont(display, _(resources.clock_font2))) == NULL)
+       if((clockfont = XLoadQueryFont(display, _(resources->clock_font))) == NULL)
+               if((clockfont = XLoadQueryFont(display, _(resources->clock_font2))) == NULL)
                        clockfont = XLoadQueryFont(display, "fixed");
 
        init_xft();
@@ -2319,19 +2387,19 @@ int BC_WindowBase::init_fonts()
                int n;
 
 // FIXME: should check the m,d,n values
-               smallfontset = XCreateFontSet(display, resources.small_fontset, &m, &n, &d);
+               smallfontset = XCreateFontSet(display, resources->small_fontset, &m, &n, &d);
                if( !smallfontset )
                        smallfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
-               mediumfontset = XCreateFontSet(display, resources.medium_fontset, &m, &n, &d);
+               mediumfontset = XCreateFontSet(display, resources->medium_fontset, &m, &n, &d);
                if( !mediumfontset )
                        mediumfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
-               largefontset = XCreateFontSet(display, resources.large_fontset, &m, &n, &d);
+               largefontset = XCreateFontSet(display, resources->large_fontset, &m, &n, &d);
                if( !largefontset )
                        largefontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
-               bigfontset = XCreateFontSet(display, resources.big_fontset, &m, &n, &d);
+               bigfontset = XCreateFontSet(display, resources->big_fontset, &m, &n, &d);
                if( !bigfontset )
                        bigfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
-               clockfontset = XCreateFontSet(display, resources.clock_fontset, &m, &n, &d);
+               clockfontset = XCreateFontSet(display, resources->clock_fontset, &m, &n, &d);
                if( !clockfontset )
                        clockfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
                if(clockfontset && bigfontset && largefontset && mediumfontset && smallfontset) {
@@ -2355,54 +2423,54 @@ void BC_WindowBase::init_xft()
 static Mutex xft_init_lock("BC_WindowBase::xft_init_lock", 0);
 xft_init_lock.lock("BC_WindowBase::init_xft");
        if(!(smallfont_xft =
-               (resources.small_font_xft[0] == '-' ?
-                       xftFontOpenXlfd(display, screen, resources.small_font_xft) :
-                       xftFontOpenName(display, screen, resources.small_font_xft))) )
+               (resources->small_font_xft[0] == '-' ?
+                       xftFontOpenXlfd(display, screen, resources->small_font_xft) :
+                       xftFontOpenName(display, screen, resources->small_font_xft))) )
                if(!(smallfont_xft =
-                       xftFontOpenXlfd(display, screen, resources.small_font_xft2)))
+                       xftFontOpenXlfd(display, screen, resources->small_font_xft2)))
                        smallfont_xft = xftFontOpenXlfd(display, screen, "fixed");
        if(!(mediumfont_xft =
-               (resources.medium_font_xft[0] == '-' ?
-                       xftFontOpenXlfd(display, screen, resources.medium_font_xft) :
-                       xftFontOpenName(display, screen, resources.medium_font_xft))) )
+               (resources->medium_font_xft[0] == '-' ?
+                       xftFontOpenXlfd(display, screen, resources->medium_font_xft) :
+                       xftFontOpenName(display, screen, resources->medium_font_xft))) )
                if(!(mediumfont_xft =
-                       xftFontOpenXlfd(display, screen, resources.medium_font_xft2)))
+                       xftFontOpenXlfd(display, screen, resources->medium_font_xft2)))
                        mediumfont_xft = xftFontOpenXlfd(display, screen, "fixed");
        if(!(largefont_xft =
-               (resources.large_font_xft[0] == '-' ?
-                       xftFontOpenXlfd(display, screen, resources.large_font_xft) :
-                       xftFontOpenName(display, screen, resources.large_font_xft))) )
+               (resources->large_font_xft[0] == '-' ?
+                       xftFontOpenXlfd(display, screen, resources->large_font_xft) :
+                       xftFontOpenName(display, screen, resources->large_font_xft))) )
                if(!(largefont_xft =
-                       xftFontOpenXlfd(display, screen, resources.large_font_xft2)))
+                       xftFontOpenXlfd(display, screen, resources->large_font_xft2)))
                        largefont_xft = xftFontOpenXlfd(display, screen, "fixed");
        if(!(bigfont_xft =
-               (resources.big_font_xft[0] == '-' ?
-                       xftFontOpenXlfd(display, screen, resources.big_font_xft) :
-                       xftFontOpenName(display, screen, resources.big_font_xft))) )
+               (resources->big_font_xft[0] == '-' ?
+                       xftFontOpenXlfd(display, screen, resources->big_font_xft) :
+                       xftFontOpenName(display, screen, resources->big_font_xft))) )
                if(!(bigfont_xft =
-                       xftFontOpenXlfd(display, screen, resources.big_font_xft2)))
+                       xftFontOpenXlfd(display, screen, resources->big_font_xft2)))
                        bigfont_xft = xftFontOpenXlfd(display, screen, "fixed");
        if(!(clockfont_xft =
-               (resources.clock_font_xft[0] == '-' ?
-                       xftFontOpenXlfd(display, screen, resources.clock_font_xft) :
-                       xftFontOpenName(display, screen, resources.clock_font_xft))) )
+               (resources->clock_font_xft[0] == '-' ?
+                       xftFontOpenXlfd(display, screen, resources->clock_font_xft) :
+                       xftFontOpenName(display, screen, resources->clock_font_xft))) )
                clockfont_xft = xftFontOpenXlfd(display, screen, "fixed");
 
 
        if(!(bold_smallfont_xft =
-               (resources.small_b_font_xft[0] == '-' ?
-                       xftFontOpenXlfd(display, screen, resources.small_b_font_xft) :
-                       xftFontOpenName(display, screen, resources.small_b_font_xft))) )
+               (resources->small_b_font_xft[0] == '-' ?
+                       xftFontOpenXlfd(display, screen, resources->small_b_font_xft) :
+                       xftFontOpenName(display, screen, resources->small_b_font_xft))) )
                bold_smallfont_xft = xftFontOpenXlfd(display, screen, "fixed");
        if(!(bold_mediumfont_xft =
-               (resources.medium_b_font_xft[0] == '-' ?
-                       xftFontOpenXlfd(display, screen, resources.medium_b_font_xft) :
-                       xftFontOpenName(display, screen, resources.medium_b_font_xft))) )
+               (resources->medium_b_font_xft[0] == '-' ?
+                       xftFontOpenXlfd(display, screen, resources->medium_b_font_xft) :
+                       xftFontOpenName(display, screen, resources->medium_b_font_xft))) )
                bold_mediumfont_xft = xftFontOpenXlfd(display, screen, "fixed");
        if(!(bold_largefont_xft =
-               (resources.large_b_font_xft[0] == '-' ?
-                       xftFontOpenXlfd(display, screen, resources.large_b_font_xft) :
-                       xftFontOpenName(display, screen, resources.large_b_font_xft))) )
+               (resources->large_b_font_xft[0] == '-' ?
+                       xftFontOpenXlfd(display, screen, resources->large_b_font_xft) :
+                       xftFontOpenName(display, screen, resources->large_b_font_xft))) )
                bold_largefont_xft = xftFontOpenXlfd(display, screen, "fixed");
 
        if( !smallfont_xft || !mediumfont_xft || !largefont_xft || !bigfont_xft ||
@@ -2410,14 +2478,14 @@ xft_init_lock.lock("BC_WindowBase::init_xft");
            !clockfont_xft ) {
                printf("BC_WindowBase::init_fonts: no xft fonts found:"
                        " %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n",
-                       resources.small_font_xft, smallfont_xft,
-                       resources.medium_font_xft, mediumfont_xft,
-                       resources.large_font_xft, largefont_xft,
-                       resources.big_font_xft, bigfont_xft,
-                       resources.clock_font_xft, clockfont_xft,
-                       resources.small_b_font_xft, bold_smallfont_xft,
-                       resources.medium_b_font_xft, bold_mediumfont_xft,
-                       resources.large_b_font_xft, bold_largefont_xft);
+                       resources->small_font_xft, smallfont_xft,
+                       resources->medium_font_xft, mediumfont_xft,
+                       resources->large_font_xft, largefont_xft,
+                       resources->big_font_xft, bigfont_xft,
+                       resources->clock_font_xft, clockfont_xft,
+                       resources->small_b_font_xft, bold_smallfont_xft,
+                       resources->medium_b_font_xft, bold_mediumfont_xft,
+                       resources->large_b_font_xft, bold_largefont_xft);
                get_resources()->use_xft = 0;
                exit(1);
        }
@@ -2427,6 +2495,33 @@ xft_init_lock.unlock();
 #endif // HAVE_XFT
 }
 
+void BC_WindowBase::init_glyphs()
+{
+// draw all ascii char glyphs
+//  There are problems with some/my graphics boards/drivers
+//  which cause some glyphs to be munged if draws occur while
+//  the font is being loaded.  This code fills the font caches
+//  by drawing all the ascii glyphs before the system starts.
+//  Not a fix, but much better than nothing.
+       static int inited = 0;
+       if( inited ) return;
+       XGrabServer(display);
+       XSync(display, 0);
+       inited = 1;
+       int cur_font = current_font;
+// locale encodings, needed glyphs to be preloaded
+       const char *text = _( // ascii 0x20...0x7e
+               " !\"#$%&'()*+,-./0123456789:;<=>?"
+               "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
+               "`abcdefghijklmnopqrstuvwxyz{|}~");
+       for( int font=SMALLFONT; font<=LARGEFONT; ++font ) {
+               set_font(font);
+               draw_text(5,5, text);
+       }
+       set_font(cur_font);
+       XUngrabServer(display);
+}
+
 void BC_WindowBase::init_im()
 {
        XIMStyles *xim_styles;
@@ -2435,7 +2530,11 @@ void BC_WindowBase::init_im()
        if(!(input_method = XOpenIM(display, NULL, NULL, NULL)))
        {
                printf("BC_WindowBase::init_im: Could not open input method.\n");
+               XSetLocaleModifiers("@im=local");
+               if(!(input_method = XOpenIM(display, NULL, NULL, NULL))) {
+               printf("BC_WindowBase::init_im: Could not open input method local.\n");
                exit(1);
+               }
        }
        if(XGetIMValues(input_method, XNQueryInputStyle, &xim_styles, NULL) ||
                        xim_styles == NULL)
@@ -2944,10 +3043,10 @@ int BC_WindowBase::get_text_width(int font, const char *text, int length)
        return w;
 }
 
-int BC_WindowBase::get_text_width(int font, const wchar_t *text, int length)
+int BC_WindowBase::get_text_width(int font, const wchr_t *text, int length)
 {
        int i, j, w = 0;
-       if( length < 0 ) length = wcslen(text);
+       if( length < 0 ) length = wstrlen(text);
 
        for( i=j=0; i<length && text[i]; ++i ) {
                if( text[i] != '\n' ) continue;
@@ -3036,6 +3135,39 @@ int BC_WindowBase::get_text_height(int font, const char *text)
        return h * rowh;
 }
 
+// truncate the text with ... & return a new string
+char *BC_WindowBase::get_truncated_text(int font, const char *text, int max_w)
+{
+       char *result = cstrdup(text);
+       int text_w = get_text_width(font, text);
+       int ci = -1, len = strlen(text);
+       if( text_w > max_w ) {
+// get center of string
+               int cx = text_w/2, best = INT_MAX;
+               for( int i=1; i<=len; ++i ) {
+                       int cw = get_text_width(font, text, i);
+                       if( abs(cw-cx) < abs(best-cx) ) {
+                               best = cw;  ci = i;
+                       }
+               }
+       }
+       if( ci > 0 && ci < len-1 ) {
+// insert ... in the center
+               result[ci-1] = result[ci] = result[ci+1] = '.';
+
+               while( ci-2>=0 && ci+2<(int)strlen(result) &&
+                      get_text_width(font, result) > max_w ) {
+// take away a character from the longer side
+                       int left_w = get_text_width(font, result, ci-2);
+                       int right_w = get_text_width(font, result + ci+3);
+                       int i = left_w > right_w ? --ci-1 : ci+2;
+                       while( (result[i]=result[i+1])!=0 ) ++i;
+               }
+       }
+
+       return result;
+}
+
 BC_Bitmap* BC_WindowBase::new_bitmap(int w, int h, int color_model)
 {
        if(color_model < 0) color_model = top_level->get_color_model();
@@ -3081,6 +3213,7 @@ int BC_WindowBase::accel_available(int color_model, int lock_it)
 
 int BC_WindowBase::grab_port_id(int color_model)
 {
+#ifdef HAVE_XV
        if( !get_resources()->use_xvideo ||     // disabled
            !get_resources()->use_shm )         // Only local server is fast enough.
                return -1;
@@ -3127,8 +3260,10 @@ int BC_WindowBase::grab_port_id(int color_model)
        }
 
        XvFreeAdaptorInfo(info);
-
        return xvideo_port_id;
+#else
+       return -1;
+#endif
 }
 
 
@@ -3376,17 +3511,36 @@ void BC_WindowBase::close(int return_value)
 
 int BC_WindowBase::grab(BC_WindowBase *window)
 {
-       if( window->active_grab && this != window->active_grab ) return 0;
-       window->active_grab = this;
-       this->grab_active = window;
-       return 1;
+       int ret = 0;
+       BC_WindowBase *grab_window = window->active_grab;
+       if( grab_window ) {
+               int locked = get_window_lock();
+               if( locked ) unlock_window();
+               grab_window->lock_window("BC_WindowBase::grab(BC_WindowBase");
+               grab_window->handle_ungrab();
+               grab_window->unlock_window();
+               if( locked ) lock_window("BC_WindowBase::grab(BC_WindowBase");
+       }
+       window->grab_lock->lock("BC_WindowBase::grab");
+       if( !window->active_grab ) {
+               window->active_grab = this;
+               this->grab_active = window;
+               ret = 1;
+       }
+       window->grab_lock->unlock();
+       return ret;
 }
 int BC_WindowBase::ungrab(BC_WindowBase *window)
 {
-       if( window->active_grab && this != window->active_grab ) return 0;
-       window->active_grab = 0;
-       this->grab_active = 0;
-       return 1;
+       int ret = 0;
+       window->grab_lock->lock("BC_WindowBase::ungrab");
+       if( this == window->active_grab ) {
+               window->active_grab = 0;
+               this->grab_active = 0;
+               ret = 1;
+       }
+       window->grab_lock->unlock();
+       return ret;
 }
 int BC_WindowBase::grab_event_count()
 {
@@ -3622,12 +3776,12 @@ int BC_WindowBase::get_color_model()
 
 BC_Resources* BC_WindowBase::get_resources()
 {
-       return &BC_WindowBase::resources;
+       return BC_WindowBase::resources;
 }
 
 BC_Synchronous* BC_WindowBase::get_synchronous()
 {
-       return BC_WindowBase::resources.get_synchronous();
+       return BC_WindowBase::resources->get_synchronous();
 }
 
 int BC_WindowBase::get_bg_color()
@@ -3868,9 +4022,9 @@ void BC_WindowBase::get_pop_cursor(int &px, int &py, int lock_window)
        get_abs_cursor(px, py, lock_window);
        if( px < xmargin ) px = xmargin;
        if( py < ymargin ) py = ymargin;
-       int wd = get_screen_w(lock_window,-1) - xmargin;
+       int wd = get_screen_x(lock_window,-1) + get_screen_w(lock_window,-1) - xmargin;
        if( px > wd ) px = wd;
-       int ht = get_screen_h(lock_window,-1) - ymargin;
+       int ht = get_screen_y(lock_window,-1) + get_screen_h(lock_window,-1) - ymargin;
        if( py > ht ) py = ht;
 }
 int BC_WindowBase::get_pop_cursor_x(int lock_window)
@@ -3971,6 +4125,12 @@ int BC_WindowBase::is_event_win()
        return this->win == top_level->event_win;
 }
 
+int BC_WindowBase::is_tooltip_event_win()
+{
+       return this->win == top_level->event_win ||
+               tooltip_popup && tooltip_popup->win == top_level->event_win;
+}
+
 void BC_WindowBase::set_dragging(int value)
 {
        is_dragging = value;
@@ -4006,7 +4166,7 @@ int BC_WindowBase::ctrl_down()
        return top_level->ctrl_mask;
 }
 
-wchar_t* BC_WindowBase::get_wkeystring(int *length)
+wchr_t* BC_WindowBase::get_wkeystring(int *length)
 {
        if(length)
                *length = top_level->wkey_string_length;
@@ -4181,6 +4341,8 @@ void BC_WindowBase::set_force_tooltip(int v)
 
 int BC_WindowBase::raise_window(int do_flush)
 {
+       if( hidden ) return 1;
+       if( wait_viewable(500) ) return 1;
        XRaiseWindow(top_level->display, win);
        if(do_flush) XFlush(top_level->display);
        return 0;
@@ -4237,6 +4399,26 @@ void BC_WindowBase::set_title(const char *text, int utf8)
        flush();
 }
 
+// must be RGBA8888
+void BC_WindowBase::set_net_icon(VFrame *data)
+{
+       int width = data->get_w(), height = data->get_h();
+       int size = 2 + width * height;
+       unsigned long *icon_data = new unsigned long[size];
+       unsigned long *lp = icon_data;
+       *lp++ = width;  *lp++ = height;
+       uint8_t **rows = data->get_rows();
+       for( int y=0; y<height; ++y ) {
+               unsigned *up = (unsigned *)rows[y];
+               for( int x=0; x<width; ++x )
+                       *lp++ = *(unsigned *)up++;
+       }
+       Atom NetWMIcon = XInternAtom(display, "_NET_WM_ICON", True);
+       XChangeProperty(top_level->display, top_level->win, NetWMIcon,
+                XA_CARDINAL, 32, PropModeReplace, (unsigned char *)icon_data, size);
+       delete [] icon_data;
+}
+
 const char *BC_WindowBase::get_title()
 {
        return title;
@@ -4258,31 +4440,62 @@ int BC_WindowBase::set_icon(VFrame *data)
        icon_pixmap = new BC_Pixmap(top_level, data, PIXMAP_ALPHA, 1);
 
        if(icon_window) delete icon_window;
-       icon_window = new BC_Popup(this,
-               (int)BC_INFINITY,
-               (int)BC_INFINITY,
-               icon_pixmap->get_w(),
-               icon_pixmap->get_h(),
-               -1,
-               1, // All windows are hidden initially
+       icon_window = new BC_Popup(this, 0, 0,
+               icon_pixmap->get_w(), icon_pixmap->get_h(),
+               -1, 1, // All windows are hidden initially
                icon_pixmap);
 
-       XWMHints wm_hints;
-       wm_hints.flags = WindowGroupHint | IconPixmapHint | IconMaskHint | IconWindowHint;
+       XWMHints wm_hints;  memset(&wm_hints, 0, sizeof(wm_hints));
+       wm_hints.flags = IconPixmapHint; // | IconMaskHint | IconWindowHint;
        wm_hints.icon_pixmap = icon_pixmap->get_pixmap();
        wm_hints.icon_mask = icon_pixmap->get_alpha();
        wm_hints.icon_window = icon_window->win;
-       wm_hints.window_group = XGroupLeader;
+       if( XGroupLeader ) {
+               wm_hints.flags |= WindowGroupHint;
+               wm_hints.window_group = XGroupLeader;
+       }
 
 // for(int i = 0; i < 1000; i++)
 // printf("02x ", icon_pixmap->get_alpha()->get_row_pointers()[0][i]);
 // printf("\n");
 
        XSetWMHints(top_level->display, top_level->win, &wm_hints);
+
+       set_net_icon(data);
        XSync(top_level->display, 0);
        return 0;
 }
 
+void BC_WindowBase::init_resources(float scale)
+{
+       if( resources ) return;
+       XInitThreads();
+       const char *env = getenv("BC_SCALE");
+       if( env ) scale = atof(env);
+       float x_scale = 1, y_scale = 1;
+       if( scale <= 0 ) {
+               BC_DisplayInfo info;
+               int wx, wy, ww, wh;
+               int cins = info.xinerama_big_screen();
+               if( !info.xinerama_geometry(cins, wx, wy, ww, wh) ) {
+                       int sh = ww * 9 / 16;
+                       int sw = wh * 16 / 9;
+                       if( sw < ww ) ww = sw;
+                       if( sh < wh ) wh = sh;
+                       if( (x_scale = ww/1920.) < 1 ) x_scale = 1;
+                       if( (y_scale = wh/1080.) < 1 ) y_scale = 1;
+               }
+       }
+       else
+               x_scale = y_scale = scale;
+       // constructor sets BC_WindowBase::resources
+       new BC_Resources(x_scale, y_scale);
+}
+void BC_WindowBase::finit_resources()
+{
+       delete resources;  resources = 0;
+}
+
 int BC_WindowBase::set_w(int w)
 {
        this->w = w;
@@ -4297,7 +4510,6 @@ int BC_WindowBase::set_h(int h)
 
 int BC_WindowBase::load_defaults(BC_Hash *defaults)
 {
-       BC_Resources *resources = get_resources();
        char string[BCTEXTLEN];
        int newest_id = - 1;
        for(int i = 0; i < FILEBOX_HISTORY_SIZE; i++)
@@ -4330,7 +4542,6 @@ int BC_WindowBase::load_defaults(BC_Hash *defaults)
 
 int BC_WindowBase::save_defaults(BC_Hash *defaults)
 {
-       BC_Resources *resources = get_resources();
        char string[BCTEXTLEN];
        for(int i = 0; i < FILEBOX_HISTORY_SIZE; i++)
        {
@@ -4552,3 +4763,15 @@ void BC_WindowBase::focus()
                XSetInputFocus(top_level->display, top_level->win, RevertToParent, CurrentTime);
 }
 
+int BC_WindowBase::wait_viewable(int ms)
+{
+       Timer timer;
+       XWindowAttributes xwa;
+       do {
+               XGetWindowAttributes(top_level->display, top_level->win, &xwa);
+               if( xwa.map_state == IsViewable ) return 0;
+               usleep(10000);
+       } while( timer.get_difference() < ms );
+       return 1;
+}
+