no longer need ffmpeg patch0 which was for Termux
[goodguy/cinelerra.git] / cinelerra-5.1 / guicast / bcwindowbase.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 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 "bcbitmap.h"
23 #include "bcclipboard.h"
24 #include "bcdisplay.h"
25 #include "bcdisplayinfo.h"
26 #include "bcmenubar.h"
27 #include "bcpixmap.h"
28 #include "bcpopup.h"
29 #include "bcpopupmenu.h"
30 #include "bcrepeater.h"
31 #include "bcresources.h"
32 #include "bcsignals.h"
33 #include "bcsubwindow.h"
34 #include "bcsynchronous.h"
35 #include "bctimer.h"
36 #include "bcwindowbase.h"
37 #include "bcwindowevents.h"
38 #include "bccmodels.h"
39 #include "bccolors.h"
40 #include "condition.h"
41 #include "cursors.h"
42 #include "bchash.h"
43 #include "fonts.h"
44 #include "keys.h"
45 #include "language.h"
46 #include "mutex.h"
47 #include "sizes.h"
48 #include "vframe.h"
49 #include "workarounds.h"
50
51 #ifdef HAVE_GL
52 #include <GL/gl.h>
53 #endif
54 #include <string.h>
55 #include <unistd.h>
56 #include <wchar.h>
57 #include <typeinfo>
58
59 #include <X11/extensions/Xinerama.h>
60 #include <X11/extensions/Xvlib.h>
61 #include <X11/extensions/shape.h>
62 #include <X11/XF86keysym.h>
63 #include <X11/Sunkeysym.h>
64
65 BC_ResizeCall::BC_ResizeCall(int w, int h)
66 {
67         this->w = w;
68         this->h = h;
69 }
70
71
72 int BC_WindowBase::shm_completion_event = -1;
73 BC_Resources *BC_WindowBase::resources = 0;
74 Window XGroupLeader = 0;
75
76 Mutex BC_KeyboardHandlerLock::keyboard_listener_mutex("keyboard_listener",0);
77 ArrayList<BC_KeyboardHandler*> BC_KeyboardHandler::listeners;
78
79 BC_WindowBase::BC_WindowBase()
80 {
81 //printf("BC_WindowBase::BC_WindowBase 1\n");
82         BC_WindowBase::initialize();
83 }
84
85 BC_WindowBase::~BC_WindowBase()
86 {
87 #ifdef SINGLE_THREAD
88         BC_Display::lock_display("BC_WindowBase::~BC_WindowBase");
89 #else
90         if(window_type == MAIN_WINDOW)
91                 lock_window("BC_WindowBase::~BC_WindowBase");
92 #endif
93
94 #ifdef HAVE_LIBXXF86VM
95         if(window_type == VIDMODE_SCALED_WINDOW && vm_switched) {
96                 restore_vm();
97         }
98 #endif
99         is_deleting = 1;
100
101         hide_tooltip();
102         if(window_type != MAIN_WINDOW)
103         {
104 // stop event input
105                 XSelectInput(top_level->display, this->win, 0);
106                 XSync(top_level->display,0);
107 #ifndef SINGLE_THREAD
108                 top_level->dequeue_events(win);
109 #endif
110 // drop active window refs to this
111                 if(top_level->active_menubar == this) top_level->active_menubar = 0;
112                 if(top_level->active_popup_menu == this) top_level->active_popup_menu = 0;
113                 if(top_level->active_subwindow == this) top_level->active_subwindow = 0;
114 // drop motion window refs to this
115                 if(top_level->motion_events && top_level->last_motion_win == this->win)
116                         top_level->motion_events = 0;
117
118 // Remove pointer from parent window to this
119                 parent_window->subwindows->remove(this);
120         }
121
122         if(grab_active) grab_active->active_grab = 0;
123         if(icon_window) delete icon_window;
124         if(window_type == POPUP_WINDOW)
125                 parent_window->remove_popup(this);
126
127 // Delete the subwindows
128         if(subwindows)
129         {
130                 while(subwindows->total)
131                 {
132 // Subwindow removes its own pointer
133                         delete subwindows->values[0];
134                 }
135                 delete subwindows;
136         }
137
138         delete pixmap;
139
140 //printf("delete glx=%08x, win=%08x %s\n", (unsigned)glx_win, (unsigned)win, title);
141 #ifdef HAVE_GL
142         if( get_resources()->get_synchronous() && glx_win != 0 ) {
143                 if( window_type == MAIN_WINDOW )
144                         unlock_window();
145                 get_resources()->get_synchronous()->delete_window(this);
146                 if( window_type == MAIN_WINDOW )
147                         lock_window("BC_WindowBase::delete_window");
148         }
149 #endif
150         XDestroyWindow(top_level->display, win);
151
152         if(bg_pixmap && !shared_bg_pixmap) delete bg_pixmap;
153         if(icon_pixmap) delete icon_pixmap;
154         if(temp_bitmap) delete temp_bitmap;
155         top_level->active_bitmaps.remove_buffers(this);
156         if(_7segment_pixmaps)
157         {
158                 for(int i = 0; i < TOTAL_7SEGMENT; i++)
159                         delete _7segment_pixmaps[i];
160
161                 delete [] _7segment_pixmaps;
162         }
163
164
165
166         if(window_type == MAIN_WINDOW)
167         {
168                 XFreeGC(display, gc);
169                 static XFontStruct *BC_WindowBase::*xfont[] = {
170                          &BC_WindowBase::smallfont,
171                          &BC_WindowBase::mediumfont,
172                          &BC_WindowBase::largefont,
173                          &BC_WindowBase::bigfont,
174                          &BC_WindowBase::clockfont,
175                 };
176                 for( int i=sizeof(xfont)/sizeof(xfont[0]); --i>=0; )
177                         XFreeFont(display, this->*xfont[i]);
178
179 #ifdef HAVE_XFT
180 // prevents a bug when Xft closes with unrefd fonts
181                 FcPattern *defaults = FcPatternCreate();
182                 FcPatternAddInteger(defaults, "maxunreffonts", 0);
183                 XftDefaultSet(display, defaults);
184
185                 static void *BC_WindowBase::*xft_font[] = {
186                          &BC_WindowBase::smallfont_xft,
187                          &BC_WindowBase::mediumfont_xft,
188                          &BC_WindowBase::largefont_xft,
189                          &BC_WindowBase::bigfont_xft,
190                          &BC_WindowBase::bold_smallfont_xft,
191                          &BC_WindowBase::bold_mediumfont_xft,
192                          &BC_WindowBase::bold_largefont_xft,
193                          &BC_WindowBase::clockfont_xft,
194                 };
195                 for( int i=sizeof(xft_font)/sizeof(xft_font[0]); --i>=0; ) {
196                         XftFont *xft = (XftFont *)(this->*xft_font[i]);
197                         if( xft ) xftFontClose (display, xft);
198                 }
199 #endif
200                 finit_im();
201                 flush();
202                 sync_display();
203
204                 if( xinerama_info )
205                         XFree(xinerama_info);
206                 xinerama_screens = 0;
207                 xinerama_info = 0;
208                 if( xvideo_port_id >= 0 )
209                         XvUngrabPort(display, xvideo_port_id, CurrentTime);
210
211                 unlock_window();
212 // Must be last reference to display.
213 // _XftDisplayInfo needs a lock.
214                 get_resources()->create_window_lock->lock("BC_WindowBase::~BC_WindowBase");
215                 XCloseDisplay(display);
216                 get_resources()->create_window_lock->unlock();
217
218 // clipboard uses a different display connection
219                 clipboard->stop_clipboard();
220                 delete clipboard;
221         }
222
223         resize_history.remove_all_objects();
224
225 #ifndef SINGLE_THREAD
226         common_events.remove_all_objects();
227         delete event_lock;
228         delete event_condition;
229         delete init_lock;
230 #else
231         top_level->window_lock = 0;
232         BC_Display::unlock_display();
233 #endif
234         delete cursor_timer;
235
236 #if HAVE_GL
237         if( glx_fbcfgs_window ) XFree(glx_fbcfgs_window);
238         if( glx_fbcfgs_pbuffer) XFree(glx_fbcfgs_pbuffer);
239         if( glx_fbcfgs_pixmap ) XFree(glx_fbcfgs_pixmap);
240 #endif
241
242         UNSET_ALL_LOCKS(this)
243 }
244
245 int BC_WindowBase::initialize()
246 {
247         done = 0;
248         done_set = 0;
249         window_running = 0;
250         display_lock_owner = 0;
251         test_keypress = 0;
252         keys_return[0] = 0;
253         is_deleting = 0;
254         window_lock = 0;
255         resend_event_window = 0;
256         x = 0;
257         y = 0;
258         w = 0;
259         h = 0;
260         bg_color = -1;
261         line_width = 1;
262         line_dashes = 0;
263         top_level = 0;
264         parent_window = 0;
265         subwindows = 0;
266         xinerama_info = 0;
267         xinerama_screens = 0;
268         xvideo_port_id = -1;
269         video_on = 0;
270         motion_events = 0;
271         resize_events = 0;
272         translation_events = 0;
273         ctrl_mask = shift_mask = alt_mask = 0;
274         cursor_x = cursor_y = button_number = 0;
275         button_down = 0;
276         button_pressed = 0;
277         button_time1 = 0;
278         button_time2 = 0;
279         button_time3 = 0;
280         double_click = 0;
281         triple_click = 0;
282         event_win = 0;
283         last_motion_win = 0;
284         key_pressed = 0;
285         active_grab = 0;
286         grab_active = 0;
287         active_menubar = 0;
288         active_popup_menu = 0;
289         active_subwindow = 0;
290         cursor_entered = 0;
291         pixmap = 0;
292         bg_pixmap = 0;
293         _7segment_pixmaps = 0;
294         tooltip_text = 0;
295         force_tooltip = 0;
296 //      next_repeat_id = 0;
297         tooltip_popup = 0;
298         current_font = MEDIUMFONT;
299         current_color = BLACK;
300         current_cursor = ARROW_CURSOR;
301         hourglass_total = 0;
302         is_dragging = 0;
303         shared_bg_pixmap = 0;
304         icon_pixmap = 0;
305         icon_window = 0;
306         window_type = MAIN_WINDOW;
307         translation_count = 0;
308         x_correction = y_correction = 0;
309         temp_bitmap = 0;
310         tooltip_on = 0;
311         temp_cursor = 0;
312         toggle_value = 0;
313         toggle_drag = 0;
314         has_focus = 0;
315         is_hourglass = 0;
316         is_transparent = 0;
317 #ifdef HAVE_LIBXXF86VM
318         vm_switched = 0;
319 #endif
320         input_method = 0;
321         input_context = 0;
322
323         smallfont = 0;
324         mediumfont = 0;
325         largefont = 0;
326         bigfont = 0;
327         clockfont = 0;
328
329         smallfont_xft = 0;
330         mediumfont_xft = 0;
331         largefont_xft = 0;
332         bigfont_xft = 0;
333         clockfont_xft = 0;
334
335         bold_smallfont_xft = 0;
336         bold_mediumfont_xft = 0;
337         bold_largefont_xft = 0;
338 #ifdef SINGLE_THREAD
339         completion_lock = new Condition(0, "BC_WindowBase::completion_lock");
340 #else
341 // Need these right away since put_event is called before run_window sometimes.
342         event_lock = new Mutex("BC_WindowBase::event_lock");
343         event_condition = new Condition(0, "BC_WindowBase::event_condition");
344         init_lock = new Condition(0, "BC_WindowBase::init_lock");
345 #endif
346
347         cursor_timer = new Timer;
348         event_thread = 0;
349 #ifdef HAVE_GL
350         glx_fbcfgs_window = 0;  n_fbcfgs_window = 0;
351         glx_fbcfgs_pbuffer = 0; n_fbcfgs_pbuffer = 0;
352         glx_fbcfgs_pixmap = 0;  n_fbcfgs_pixmap = 0;
353
354         glx_fb_config = 0;
355         glx_win_context = 0;
356         glx_win = 0;
357 #endif
358
359         flash_enabled = 1;
360         win = 0;
361         return 0;
362 }
363
364
365
366 #define DEFAULT_EVENT_MASKS EnterWindowMask | \
367                         LeaveWindowMask | \
368                         ButtonPressMask | \
369                         ButtonReleaseMask | \
370                         PointerMotionMask | \
371                         FocusChangeMask
372
373
374 int BC_WindowBase::create_window(BC_WindowBase *parent_window, const char *title,
375                 int x, int y, int w, int h, int minw, int minh, int allow_resize,
376                 int private_color, int hide, int bg_color, const char *display_name,
377                 int window_type, BC_Pixmap *bg_pixmap, int group_it)
378 {
379         XSetWindowAttributes attr;
380         unsigned long mask;
381         XSizeHints size_hints;
382         int root_w;
383         int root_h;
384 #ifdef HAVE_LIBXXF86VM
385         int vm;
386 #endif
387
388         id = get_resources()->get_id();
389         if(parent_window) top_level = parent_window->top_level;
390         if( top_level ) lock_window("BC_WindowBase::create_window");
391         get_resources()->create_window_lock->lock("BC_WindowBase::create_window");
392
393 #ifdef HAVE_LIBXXF86VM
394         if(window_type == VIDMODE_SCALED_WINDOW)
395                 closest_vm(&vm,&w,&h);
396 #endif
397
398         this->x = x;
399         this->y = y;
400         this->w = w;
401         this->h = h;
402         this->bg_color = bg_color;
403         this->window_type = window_type;
404         this->hidden = hide;
405         this->private_color = private_color;
406         this->parent_window = parent_window;
407         this->bg_pixmap = bg_pixmap;
408         this->allow_resize = allow_resize;
409         if(display_name)
410                 strcpy(this->display_name, display_name);
411         else
412                 this->display_name[0] = 0;
413
414         put_title(title);
415         if(bg_pixmap) shared_bg_pixmap = 1;
416
417         subwindows = new BC_SubWindowList;
418
419         if(window_type == MAIN_WINDOW)
420         {
421                 top_level = this;
422                 parent_window = this;
423
424
425 #ifdef SINGLE_THREAD
426                 display = BC_Display::get_display(display_name);
427                 BC_Display::lock_display("BC_WindowBase::create_window");
428 //              BC_Display::display_global->new_window(this);
429 #else
430
431 // get the display connection
432
433 // This function must be the first Xlib
434 // function a multi-threaded program calls
435                 XInitThreads();
436                 display = init_display(display_name);
437                 if( shm_completion_event < 0 ) shm_completion_event =
438                         ShmCompletion + XShmGetEventBase(display);
439 #endif
440                 lock_window("BC_WindowBase::create_window 1");
441
442                 screen = DefaultScreen(display);
443                 rootwin = RootWindow(display, screen);
444 // window placement boundaries
445                 if( !xinerama_screens && XineramaIsActive(display) )
446                         xinerama_info = XineramaQueryScreens(display, &xinerama_screens);
447                 root_w = get_root_w(0);
448                 root_h = get_root_h(0);
449
450 #if HAVE_GL
451                 vis = get_glx_visual(display);
452                 if( !vis )
453 #endif
454                 {
455                         int mask = VisualDepthMask | VisualClassMask;
456                         static XVisualInfo vinfo;
457                         memset(&vinfo, 0, sizeof(vinfo));
458                         vinfo.depth = 24;
459                         vinfo.c_class = TrueColor;
460                         int nitems = 0;
461                         XVisualInfo *vis_info = XGetVisualInfo(display, mask, &vinfo, &nitems);
462                         vis = vis_info && nitems>0 ? vis_info[0].visual : 0;
463                         if( vis_info ) XFree(vis_info);
464                 }
465                 if( !vis )
466                         vis = DefaultVisual(display, screen);
467                 default_depth = DefaultDepth(display, screen);
468
469                 client_byte_order = (*(const u_int32_t*)"a   ") & 0x00000001;
470                 server_byte_order = (XImageByteOrder(display) == MSBFirst) ? 0 : 1;
471
472
473 // This must be done before fonts to know if antialiasing is available.
474                 init_colors();
475 // get the resources
476                 if(resources->use_shm < 0) resources->initialize_display(this);
477                 x_correction = BC_DisplayInfo::get_left_border();
478                 y_correction = BC_DisplayInfo::get_top_border();
479
480 // clamp window placement
481                 if(this->x + this->w + x_correction > root_w)
482                         this->x = root_w - this->w - x_correction;
483                 if(this->y + this->h + y_correction > root_h)
484                         this->y = root_h - this->h - y_correction;
485                 if(this->x < 0) this->x = 0;
486                 if(this->y < 0) this->y = 0;
487
488                 if(this->bg_color == -1)
489                         this->bg_color = resources->get_bg_color();
490
491 // printf("bcwindowbase 1 %s\n", title);
492 // if(window_type == MAIN_WINDOW) sleep(1);
493 // printf("bcwindowbase 10\n");
494                 init_fonts();
495                 init_gc();
496                 init_cursors();
497
498 // Create the window
499                 mask = CWEventMask | CWBackPixel | CWColormap | CWCursor;
500
501                 attr.event_mask = DEFAULT_EVENT_MASKS |
502                         StructureNotifyMask |
503                         KeyPressMask |
504                         KeyReleaseMask;
505
506                 attr.background_pixel = get_color(this->bg_color);
507                 attr.colormap = cmap;
508                 attr.cursor = get_cursor_struct(ARROW_CURSOR);
509
510                 win = XCreateWindow(display, rootwin,
511                         this->x, this->y, this->w, this->h, 0,
512                         top_level->default_depth, InputOutput,
513                         vis, mask, &attr);
514                 XGetNormalHints(display, win, &size_hints);
515
516                 size_hints.flags = PSize | PMinSize | PMaxSize;
517                 size_hints.width = this->w;
518                 size_hints.height = this->h;
519                 size_hints.min_width = allow_resize ? minw : this->w;
520                 size_hints.max_width = allow_resize ? 32767 : this->w;
521                 size_hints.min_height = allow_resize ? minh : this->h;
522                 size_hints.max_height = allow_resize ? 32767 : this->h;
523                 if(x > -BC_INFINITY && x < BC_INFINITY)
524                 {
525                         size_hints.flags |= PPosition;
526                         size_hints.x = this->x;
527                         size_hints.y = this->y;
528                 }
529                 XSetWMProperties(display, win, 0, 0, 0, 0, &size_hints, 0, 0);
530                 get_atoms();
531                 set_title(title);
532 #ifndef SINGLE_THREAD
533                 clipboard = new BC_Clipboard(this);
534                 clipboard->start_clipboard();
535 #endif
536
537
538                 if (group_it)
539                 {
540                         Atom ClientLeaderXAtom;
541                         if (XGroupLeader == 0)
542                                 XGroupLeader = win;
543                         const char *instance_name = "cinelerra";
544                         const char *class_name = "Cinelerra";
545                         XClassHint *class_hints = XAllocClassHint();
546                         class_hints->res_name = (char*)instance_name;
547                         class_hints->res_class = (char*)class_name;
548                         XSetClassHint(top_level->display, win, class_hints);
549                         XFree(class_hints);
550                         ClientLeaderXAtom = XInternAtom(display, "WM_CLIENT_LEADER", True);
551                         XChangeProperty(display, win, ClientLeaderXAtom, XA_WINDOW, 32,
552                                 PropModeReplace, (unsigned char *)&XGroupLeader, true);
553                 }
554                 init_im();
555                 set_icon(get_resources()->default_icon);
556         }
557
558 #ifdef HAVE_LIBXXF86VM
559         if(window_type == VIDMODE_SCALED_WINDOW && vm != -1)
560         {
561                 scale_vm (vm);
562                 vm_switched = 1;
563         }
564 #endif
565
566 #ifdef HAVE_LIBXXF86VM
567         if(window_type == POPUP_WINDOW || window_type == VIDMODE_SCALED_WINDOW)
568 #else
569         if(window_type == POPUP_WINDOW)
570 #endif
571         {
572                 mask = CWEventMask | CWBackPixel | CWColormap |
573                         CWOverrideRedirect | CWSaveUnder | CWCursor;
574
575                 attr.event_mask = DEFAULT_EVENT_MASKS | ExposureMask |
576                         KeyPressMask | KeyReleaseMask;
577
578                 if(this->bg_color == -1)
579                         this->bg_color = resources->get_bg_color();
580                 attr.background_pixel = top_level->get_color(bg_color);
581                 attr.colormap = top_level->cmap;
582                 if(top_level->is_hourglass)
583                         attr.cursor = top_level->get_cursor_struct(HOURGLASS_CURSOR);
584                 else
585                         attr.cursor = top_level->get_cursor_struct(ARROW_CURSOR);
586                 attr.override_redirect = True;
587                 attr.save_under = True;
588
589                 win = XCreateWindow(top_level->display,
590                         top_level->rootwin, this->x, this->y, this->w, this->h, 0,
591                         top_level->default_depth, InputOutput, top_level->vis, mask,
592                         &attr);
593                 top_level->add_popup(this);
594         }
595
596         if(window_type == SUB_WINDOW)
597         {
598                 mask = CWEventMask | CWBackPixel | CWCursor;
599                 attr.event_mask = DEFAULT_EVENT_MASKS;
600                 attr.background_pixel = top_level->get_color(this->bg_color);
601                 if(top_level->is_hourglass)
602                         attr.cursor = top_level->get_cursor_struct(HOURGLASS_CURSOR);
603                 else
604                         attr.cursor = top_level->get_cursor_struct(ARROW_CURSOR);
605                 win = XCreateWindow(top_level->display,
606                         parent_window->win, this->x, this->y, this->w, this->h, 0,
607                         top_level->default_depth, InputOutput, top_level->vis, mask,
608                         &attr);
609                 init_window_shape();
610                 if(!hidden) XMapWindow(top_level->display, win);
611         }
612
613 // Create pixmap for all windows
614         pixmap = new BC_Pixmap(this, this->w, this->h);
615
616 // Set up options for main window
617         if(window_type == MAIN_WINDOW)
618         {
619                 if(get_resources()->bg_image && !bg_pixmap && bg_color < 0)
620                 {
621                         this->bg_pixmap = new BC_Pixmap(this,
622                                 get_resources()->bg_image,
623                                 PIXMAP_OPAQUE);
624                 }
625
626                 if(!hidden) show_window();
627                 init_glyphs();
628         }
629
630         draw_background(0, 0, this->w, this->h);
631
632         flash(-1, -1, -1, -1, 0);
633
634 // Set up options for popup window
635 #ifdef HAVE_LIBXXF86VM
636         if(window_type == POPUP_WINDOW || window_type == VIDMODE_SCALED_WINDOW)
637 #else
638         if(window_type == POPUP_WINDOW)
639 #endif
640         {
641                 init_window_shape();
642                 if(!hidden) show_window();
643         }
644         get_resources()->create_window_lock->unlock();
645         unlock_window();
646
647         return 0;
648 }
649
650 Display* BC_WindowBase::init_display(const char *display_name)
651 {
652         Display* display;
653
654         if(display_name && display_name[0] == 0) display_name = NULL;
655         if((display = XOpenDisplay(display_name)) == NULL) {
656                 printf("BC_WindowBase::init_display: cannot connect to X server %s\n",
657                         display_name);
658                 if(getenv("DISPLAY") == NULL) {
659                         printf(_("'DISPLAY' environment variable not set.\n"));
660                         exit(1);
661                 }
662 // Try again with default display.
663                 if((display = XOpenDisplay(0)) == NULL) {
664                         printf("BC_WindowBase::init_display: cannot connect to default X server.\n");
665                         exit(1);
666                 }
667         }
668
669         static int xsynch = -1;
670         if( xsynch < 0 ) {
671                 const char *cp = getenv("CIN_XSYNCH");
672                 xsynch = !cp ? 0 : atoi(cp);
673         }
674         if( xsynch > 0 )
675                 XSynchronize(display, True);
676
677         return display;
678 }
679
680 Display* BC_WindowBase::get_display()
681 {
682         return top_level->display;
683 }
684
685 int BC_WindowBase::get_screen()
686 {
687         return top_level->screen;
688 }
689
690 int BC_WindowBase::run_window()
691 {
692         done_set = done = 0;
693         return_value = 0;
694
695
696 // Events may have been sent before run_window so can't initialize them here.
697
698 #ifdef SINGLE_THREAD
699         set_repeat(get_resources()->tooltip_delay);
700         BC_Display::display_global->new_window(this);
701
702 // If the first window created, run the display loop in this thread.
703         if(BC_Display::display_global->is_first(this))
704         {
705                 BC_Display::unlock_display();
706                 BC_Display::display_global->loop();
707         }
708         else
709         {
710                 BC_Display::unlock_display();
711                 completion_lock->lock("BC_WindowBase::run_window");
712         }
713
714         BC_Display::lock_display("BC_WindowBase::run_window");
715         BC_Display::display_global->delete_window(this);
716
717         unset_all_repeaters();
718         hide_tooltip();
719         BC_Display::unlock_display();
720
721 #else // SINGLE_THREAD
722
723
724
725 // Start tooltips
726         set_repeat(get_resources()->tooltip_delay);
727
728 // Start X server events
729         event_thread = new BC_WindowEvents(this);
730         event_thread->start();
731
732 // Release wait lock
733         window_running = 1;
734         init_lock->unlock();
735
736 // Handle common events
737         while( !done ) {
738                 dispatch_event();
739         }
740
741         unset_all_repeaters();
742         hide_tooltip();
743         delete event_thread;
744         event_thread = 0;
745         event_condition->reset();
746         common_events.remove_all_objects();
747         window_running = 0;
748         done = 0;
749
750 #endif // SINGLE_THREAD
751
752         return return_value;
753 }
754
755 int BC_WindowBase::get_key_masks(unsigned int key_state)
756 {
757 // printf("BC_WindowBase::get_key_masks %llx\n",
758 // event->xkey.state);
759         ctrl_mask = (key_state & ControlMask) ? 1 : 0;  // ctrl key down
760         shift_mask = (key_state & ShiftMask) ? 1 : 0;   // shift key down
761         alt_mask = (key_state & Mod1Mask) ? 1 : 0;      // alt key down
762         return 0;
763 }
764
765
766 void BC_WindowBase::add_keyboard_listener(int(BC_WindowBase::*handler)(BC_WindowBase *))
767 {
768         BC_KeyboardHandlerLock set;
769         BC_KeyboardHandler::listeners.append(new BC_KeyboardHandler(handler, this));
770 }
771
772 void BC_WindowBase::del_keyboard_listener(int(BC_WindowBase::*handler)(BC_WindowBase *))
773 {
774         BC_KeyboardHandlerLock set;
775         int i = BC_KeyboardHandler::listeners.size();
776         while( --i >= 0 && BC_KeyboardHandler::listeners[i]->handler!=handler );
777         if( i >= 0 ) BC_KeyboardHandler::listeners.remove_object_number(i);
778 }
779
780 int BC_KeyboardHandler::run_event(BC_WindowBase *wp)
781 {
782         int result = (win->*handler)(wp);
783         return result;
784 }
785
786 int BC_KeyboardHandler::run_listeners(BC_WindowBase *wp)
787 {
788         int result = 0;
789         BC_KeyboardHandlerLock set;
790         for( int i=0; !result && i<listeners.size(); ++i ) {
791                 BC_KeyboardHandler *listener = listeners[i];
792                 result = listener->run_event(wp);
793         }
794         return result;
795 }
796
797 void BC_KeyboardHandler::kill_grabs()
798 {
799         BC_KeyboardHandlerLock set;
800         for( int i=0; i<listeners.size(); ++i ) {
801                 BC_WindowBase *win = listeners[i]->win;
802                 if( win->get_window_type() != POPUP_WINDOW ) continue;
803                 ((BC_Popup *)win)->ungrab_keyboard();
804         }
805 }
806
807 void BC_ActiveBitmaps::reque(XEvent *event)
808 {
809         XShmCompletionEvent *shm_ev = (XShmCompletionEvent *)event;
810         ShmSeg shmseg = shm_ev->shmseg;
811         Drawable drawable = shm_ev->drawable;
812 //printf("BC_BitmapImage::reque %08lx\n",shmseg);
813         active_lock.lock("BC_BitmapImage::reque");
814         BC_BitmapImage *bfr = first;
815         while( bfr && bfr->get_shmseg() != shmseg ) bfr = bfr->next;
816         if( bfr && bfr->drawable == drawable )
817                 remove_pointer(bfr);
818         active_lock.unlock();
819         if( !bfr ) {
820 // sadly, X reports two drawable completions and creates false reporting, so no boobytrap
821 //              printf("BC_BitmapImage::reque missed shmseg %08x, drawable %08x\n",
822 //                       (int)shmseg, (int)drawable);
823                 return;
824         }
825         if( bfr->drawable != drawable ) return;
826         if( bfr->is_zombie() ) { --BC_Bitmap::zombies; delete bfr; return; }
827         bfr->bitmap->reque(bfr);
828 }
829
830 void BC_ActiveBitmaps::insert(BC_BitmapImage *bfr, Drawable pixmap)
831 {
832         active_lock.lock("BC_BitmapImage::insert");
833         bfr->drawable = pixmap;
834         append(bfr);
835         active_lock.unlock();
836 }
837
838 void BC_ActiveBitmaps::remove_buffers(BC_WindowBase *wdw)
839 {
840         active_lock.lock("BC_ActiveBitmaps::remove");
841         for( BC_BitmapImage *nxt=0, *bfr=first; bfr; bfr=nxt ) {
842                 nxt = bfr->next;
843                 if( bfr->is_zombie() ) { --BC_Bitmap::zombies; delete bfr; continue; }
844                 if( bfr->bitmap->parent_window == wdw ) remove_pointer(bfr);
845         }
846         active_lock.unlock();
847 }
848
849 BC_ActiveBitmaps::BC_ActiveBitmaps()
850 {
851 }
852
853 BC_ActiveBitmaps::~BC_ActiveBitmaps()
854 {
855 }
856
857
858
859 int BC_WindowBase::keysym_lookup(XEvent *event)
860 {
861         for( int i = 0; i < KEYPRESSLEN; ++i ) keys_return[i] = 0;
862         for( int i = 0; i < 4; ++i ) wkey_string[i] = 0;
863
864         if( event->xany.send_event && !event->xany.serial ) {
865                 keysym = (KeySym) event->xkey.keycode;
866                 keys_return[0] = keysym;
867                 return 0;
868         }
869         wkey_string_length = 0;
870
871         if( input_context ) {
872                 wkey_string_length = XwcLookupString(input_context,
873                         (XKeyEvent*)event, wkey_string, 4, &keysym, 0);
874 //printf("keysym_lookup 1 %d %d %lx %x %x %x %x\n", wkey_string_length, keysym,
875 //  wkey_string[0], wkey_string[1], wkey_string[2], wkey_string[3]);
876
877                 Status stat;
878                 int ret = Xutf8LookupString(input_context, (XKeyEvent*)event,
879                                 keys_return, KEYPRESSLEN, &keysym, &stat);
880 //printf("keysym_lookup 2 %d %d %lx %x %x\n", ret, stat, keysym, keys_return[0], keys_return[1]);
881                 if( stat == XLookupBoth ) return ret;
882                 if( stat == XLookupKeySym ) return 0;
883         }
884         int ret = XLookupString((XKeyEvent*)event, keys_return, KEYPRESSLEN, &keysym, 0);
885         wkey_string_length = ret;
886         for( int i=0; i<ret; ++i ) wkey_string[i] = keys_return[i];
887         return ret;
888 }
889
890 pthread_t locking_task = (pthread_t)-1L;
891 int locking_event = -1;
892 int locking_message = -1;
893
894 int BC_WindowBase::dispatch_event()
895 {
896         Window tempwin;
897         int result;
898         XClientMessageEvent *ptr;
899         int cancel_resize, cancel_translation;
900         volatile static int debug = 0;
901         XEvent *event;
902
903         key_pressed = 0;
904
905 #ifndef SINGLE_THREAD
906 // If an event is waiting get it, otherwise
907 // wait for next event only if there are no compressed events.
908         if(get_event_count() ||
909                 (!motion_events && !resize_events && !translation_events))
910         {
911                 event = get_event();
912 // Lock out window deletions
913                 lock_window("BC_WindowBase::dispatch_event 1");
914 locking_event = event->type;
915 locking_task = pthread_self();
916 locking_message = event->xclient.message_type;
917         }
918         else
919 // Handle compressed events
920         {
921                 lock_window("BC_WindowBase::dispatch_event 2");
922                 if(resize_events)
923                         dispatch_resize_event(last_resize_w, last_resize_h);
924                 if(motion_events)
925                         dispatch_motion_event();
926                 if(translation_events)
927                         dispatch_translation_event();
928
929                 unlock_window();
930                 return 0;
931         }
932
933 #endif
934
935
936
937
938 if( debug && event->type != ClientMessage ) {
939  static const char *event_names[] = {
940   "Reply", "Error", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease", "MotionNotify",
941   "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut", "KeymapNotify", "Expose", "GraphicsExpose",
942   "NoExpose", "VisibilityNotify", "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify",
943   "MapRequest", "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
944   "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify", "SelectionClear",
945   "SelectionRequest", "SelectionNotify", "ColormapNotify", "ClientMessage", "MappingNotify",
946   "GenericEvent", "LASTEvent",
947  };
948  const int nevents = sizeof(event_names)/sizeof(event_names[0]);
949
950  printf("BC_WindowBase::dispatch_event %d %s %p %d (%s)\n", __LINE__,
951   title, event, event->type, event->type>=0 && event->type<nevents ?
952    event_names[event->type] : "Unknown");
953 }
954
955         if( active_grab ) {
956                 unlock_window();
957                 active_grab->lock_window("BC_WindowBase::dispatch_event 3");
958                 result = active_grab->grab_event(event);
959                 active_grab->unlock_window();
960                 if( result ) return result;
961                 lock_window("BC_WindowBase::dispatch_event 4");
962         }
963
964         switch(event->type) {
965         case ClientMessage:
966 // Clear the resize buffer
967                 if( resize_events )
968                         dispatch_resize_event(last_resize_w, last_resize_h);
969 // Clear the motion buffer since this can clear the window
970                 if( motion_events )
971                         dispatch_motion_event();
972
973                 ptr = (XClientMessageEvent*)event;
974                 if( ptr->message_type == ProtoXAtom &&
975                     (Atom)ptr->data.l[0] == DelWinXAtom ) {
976                         close_event();
977                 }
978                 else if( ptr->message_type == RepeaterXAtom ) {
979                         dispatch_repeat_event(ptr->data.l[0]);
980                 }
981                 else if( ptr->message_type == SetDoneXAtom ) {
982                         done = 1;
983                 }
984                 else {
985                         receive_custom_xatoms((xatom_event *)ptr);
986                 }
987                 break;
988
989         case FocusIn:
990                 has_focus = 1;
991                 dispatch_focus_in();
992                 break;
993
994         case FocusOut:
995                 has_focus = 0;
996                 dispatch_focus_out();
997                 break;
998
999 // Maximized
1000         case MapNotify:
1001                 break;
1002
1003 // Minimized
1004         case UnmapNotify:
1005                 break;
1006
1007         case ButtonPress:
1008                 if(motion_events)
1009                 {
1010                         dispatch_motion_event();
1011                 }
1012                 get_key_masks(event->xbutton.state);
1013                 cursor_x = event->xbutton.x;
1014                 cursor_y = event->xbutton.y;
1015                 button_number = event->xbutton.button;
1016
1017 //printf("BC_WindowBase::dispatch_event %d %d\n", __LINE__, button_number);
1018                 event_win = event->xany.window;
1019                 if (button_number < 6) {
1020                         if(button_number < 4)
1021                                 button_down = 1;
1022                         button_pressed = event->xbutton.button;
1023                         button_time1 = button_time2;
1024                         button_time2 = button_time3;
1025                         button_time3 = event->xbutton.time;
1026                         drag_x = cursor_x;
1027                         drag_y = cursor_y;
1028                         drag_win = event_win;
1029                         drag_x1 = cursor_x - get_resources()->drag_radius;
1030                         drag_x2 = cursor_x + get_resources()->drag_radius;
1031                         drag_y1 = cursor_y - get_resources()->drag_radius;
1032                         drag_y2 = cursor_y + get_resources()->drag_radius;
1033
1034                         if((long)(button_time3 - button_time1) < resources->double_click * 2)
1035                         {
1036                                 triple_click = 1;
1037                                 button_time3 = button_time2 = button_time1 = 0;
1038                         }
1039                         if((long)(button_time3 - button_time2) < resources->double_click)
1040                         {
1041                                 double_click = 1;
1042 //                              button_time3 = button_time2 = button_time1 = 0;
1043                         }
1044                         else
1045                         {
1046                                 triple_click = 0;
1047                                 double_click = 0;
1048                         }
1049
1050                         dispatch_button_press();
1051                 }
1052                 break;
1053
1054         case ButtonRelease:
1055                 if(motion_events)
1056                 {
1057                         dispatch_motion_event();
1058                 }
1059                 get_key_masks(event->xbutton.state);
1060                 button_number = event->xbutton.button;
1061                 event_win = event->xany.window;
1062                 if (button_number < 6)
1063                 {
1064                         if(button_number < 4)
1065                                 button_down = 0;
1066 //printf("BC_WindowBase::dispatch_event %d %d\n", __LINE__, button_number);
1067
1068                         dispatch_button_release();
1069                 }
1070                 break;
1071
1072         case Expose:
1073                 event_win = event->xany.window;
1074                 result = 0;
1075                 for( int i=0; !result && i<popups.size(); ++i ) {  // popups take focus
1076                         if( popups[i]->win == event_win )
1077                                 result = popups[i]->dispatch_expose_event();
1078                 }
1079                 if( !result )
1080                         result = dispatch_expose_event();
1081                 break;
1082
1083         case MotionNotify:
1084                 get_key_masks(event->xmotion.state);
1085 // Dispatch previous motion event if this is a subsequent motion from a different window
1086                 if(motion_events && last_motion_win != event->xany.window)
1087                 {
1088                         dispatch_motion_event();
1089                 }
1090
1091 // Buffer the current motion
1092                 motion_events = 1;
1093                 last_motion_state = event->xmotion.state;
1094                 last_motion_x = event->xmotion.x;
1095                 last_motion_y = event->xmotion.y;
1096                 last_motion_win = event->xany.window;
1097                 break;
1098
1099         case ConfigureNotify:
1100 // printf("BC_WindowBase::dispatch_event %d win=%p this->win=%p\n",
1101 // __LINE__,
1102 // event->xany.window,
1103 // win);
1104 // dump_windows();
1105                 XTranslateCoordinates(top_level->display,
1106                         top_level->win,
1107                         top_level->rootwin,
1108                         0,
1109                         0,
1110                         &last_translate_x,
1111                         &last_translate_y,
1112                         &tempwin);
1113                 last_resize_w = event->xconfigure.width;
1114                 last_resize_h = event->xconfigure.height;
1115
1116                 cancel_resize = 0;
1117                 cancel_translation = 0;
1118
1119 // Resize history prevents responses to recursive resize requests
1120                 for(int i = 0; i < resize_history.total && !cancel_resize; i++)
1121                 {
1122                         if(resize_history.values[i]->w == last_resize_w &&
1123                                 resize_history.values[i]->h == last_resize_h)
1124                         {
1125                                 delete resize_history.values[i];
1126                                 resize_history.remove_number(i);
1127                                 cancel_resize = 1;
1128                         }
1129                 }
1130
1131                 if(last_resize_w == w && last_resize_h == h)
1132                         cancel_resize = 1;
1133
1134                 if(!cancel_resize)
1135                 {
1136                         resize_events = 1;
1137                 }
1138
1139                 if((last_translate_x == x && last_translate_y == y))
1140                         cancel_translation = 1;
1141
1142                 if(!cancel_translation)
1143                 {
1144                         translation_events = 1;
1145                 }
1146
1147                 translation_count++;
1148                 break;
1149
1150         case KeyPress:
1151                 get_key_masks(event->xkey.state);
1152                 keys_return[0] = 0;  keysym = -1;
1153                 if(XFilterEvent(event, win)) {
1154                         break;
1155                 }
1156                 if( keysym_lookup(event) < 0 ) {
1157                         printf("keysym %x\n", (uint32_t)keysym);
1158                         break;
1159                 }
1160
1161 //printf("BC_WindowBase::dispatch_event %d keysym=0x%x\n",
1162 //__LINE__,
1163 //keysym);
1164
1165 // block out control keys
1166                 if(keysym > 0xffe0 && keysym < 0xffff) break;
1167 // block out Alt_GR key
1168                 if(keysym == 0xfe03) break;
1169
1170                 if(test_keypress)
1171                          printf("BC_WindowBase::dispatch_event %x\n", (uint32_t)keysym);
1172
1173 #ifdef X_HAVE_UTF8_STRING
1174 //It's Ascii or UTF8?
1175 //              if (keysym != 0xffff && (keys_return[0] & 0xff) >= 0x7f )
1176 //printf("BC_WindowBase::dispatch_event %d %02x%02x\n", __LINE__, keys_return[0], keys_return[1]);
1177
1178                 if( ((keys_return[1] & 0xff) > 0x80) &&
1179                     ((keys_return[0] & 0xff) > 0xC0) ) {
1180 //printf("BC_WindowBase::dispatch_event %d\n", __LINE__);
1181                         key_pressed = keysym & 0xff;
1182                 }
1183                 else {
1184 #endif
1185 // shuttle speed codes
1186                 if( keysym >= SKEY_MIN && keysym <= SKEY_MAX ) {
1187                         key_pressed = keysym;
1188                 }
1189                 else switch( keysym ) {
1190 // block out extra keys
1191                 case XK_Alt_L:
1192                 case XK_Alt_R:
1193                 case XK_Shift_L:
1194                 case XK_Shift_R:
1195                 case XK_Control_L:
1196                 case XK_Control_R:
1197                         key_pressed = 0;
1198                         break;
1199
1200 // Translate key codes
1201                 case XK_Return:         key_pressed = RETURN;   break;
1202                 case XK_Up:             key_pressed = UP;       break;
1203                 case XK_Down:           key_pressed = DOWN;     break;
1204                 case XK_Left:           key_pressed = LEFT;     break;
1205                 case XK_Right:          key_pressed = RIGHT;    break;
1206                 case XK_Next:           key_pressed = PGDN;     break;
1207                 case XK_Prior:          key_pressed = PGUP;     break;
1208                 case XK_BackSpace:      key_pressed = BACKSPACE; break;
1209                 case XK_Escape:         key_pressed = ESC;      break;
1210                 case XK_Tab:
1211                         if(shift_down())
1212                                 key_pressed = LEFTTAB;
1213                         else
1214                                 key_pressed = TAB;
1215                         break;
1216                 case XK_ISO_Left_Tab:   key_pressed = LEFTTAB;  break;
1217                 case XK_underscore:     key_pressed = '_';      break;
1218                 case XK_asciitilde:     key_pressed = '~';      break;
1219                 case XK_Delete:         key_pressed = DELETE;   break;
1220                 case XK_Home:           key_pressed = HOME;     break;
1221                 case XK_End:            key_pressed = END;      break;
1222
1223 // number pad
1224                 case XK_KP_Enter:       key_pressed = KPENTER;  break;
1225                 case XK_KP_Add:         key_pressed = KPPLUS;   break;
1226                 case XK_KP_Subtract:    key_pressed = KPMINUS;  break;
1227                 case XK_KP_Multiply:    key_pressed = KPSTAR;   break;
1228                 case XK_KP_Divide:      key_pressed = KPSLASH;  break;
1229                 case XK_KP_1:
1230                 case XK_KP_End:         key_pressed = KP1;      break;
1231                 case XK_KP_2:
1232                 case XK_KP_Down:        key_pressed = KP2;      break;
1233                 case XK_KP_3:
1234                 case XK_KP_Page_Down:   key_pressed = KP3;      break;
1235                 case XK_KP_4:
1236                 case XK_KP_Left:        key_pressed = KP4;      break;
1237                 case XK_KP_5:
1238                 case XK_KP_Begin:       key_pressed = KP5;      break;
1239                 case XK_KP_6:
1240                 case XK_KP_Right:       key_pressed = KP6;      break;
1241                 case XK_KP_7:
1242                 case XK_KP_Home:        key_pressed = KP7;      break;
1243                 case XK_KP_8:
1244                 case XK_KP_Up:          key_pressed = KP8;      break;
1245                 case XK_KP_9:
1246                 case XK_KP_Page_Up:     key_pressed = KP9;      break;
1247                 case XK_KP_0:
1248                 case XK_KP_Insert:      key_pressed = KPINS;    break;
1249                 case XK_KP_Decimal:
1250                 case XK_KP_Delete:      key_pressed = KPDEL;    break;
1251
1252                 case XK_F1:             key_pressed = KEY_F1;   break;
1253                 case XK_F2:             key_pressed = KEY_F2;   break;
1254                 case XK_F3:             key_pressed = KEY_F3;   break;
1255                 case XK_F4:             key_pressed = KEY_F4;   break;
1256                 case XK_F5:             key_pressed = KEY_F5;   break;
1257                 case XK_F6:             key_pressed = KEY_F6;   break;
1258                 case XK_F7:             key_pressed = KEY_F7;   break;
1259                 case XK_F8:             key_pressed = KEY_F8;   break;
1260                 case XK_F9:             key_pressed = KEY_F9;   break;
1261                 case XK_F10:            key_pressed = KEY_F10;  break;
1262                 case XK_F11:            key_pressed = KEY_F11;  break;
1263                 case XK_F12:            key_pressed = KEY_F12;  break;
1264
1265                 case XK_Menu:           key_pressed = KPMENU;   break;  /* menu */
1266 // remote control
1267 // above        case XK_KP_Enter:       key_pressed = KPENTER;  break;  /* check */
1268                 case XF86XK_MenuKB:     key_pressed = KPMENU;   break;  /* menu */
1269 // intercepted  case XF86XK_PowerDown: key_pressed = KPPOWER;   break;  /* Power */
1270                 case XF86XK_Launch1:    key_pressed = KPTV;     break;  /* TV */
1271                 case XF86XK_Launch2:    key_pressed = KPDVD;    break;  /* DVD */
1272 // intercepted  case XF86XK_WWW:        key_pressed = KPWWEB;   break;  /* WEB */
1273                 case XF86XK_Launch3:    key_pressed = KPBOOK;   break;  /* book */
1274                 case XF86XK_Launch4:    key_pressed = KPHAND;   break;  /* hand */
1275                 case XF86XK_Reply:      key_pressed = KPTMR;    break;  /* timer */
1276                 case SunXK_Front:       key_pressed = KPMAXW;   break;  /* max */
1277 // above        case XK_Left:           key_pressed = LEFT;     break;  /* left */
1278 // above        case XK_Right:          key_pressed = RIGHT;    break;  /* right */
1279 // above        case XK_Down:           key_pressed = DOWN;     break;  /* down */
1280 // above        case XK_Up:             key_pressed = UP;       break;  /* up */
1281 // above        case XK_SPACE:          key_pressed = KPSPACE;  break;  /* ok */
1282 // intercepted  case XF86XK_AudioRaiseVolume: key_pressed = KPVOLU;     break;  /* VOL + */
1283 // intercepted  case XF86XK_AudioMute: key_pressed = KPMUTE;    break;  /* MUTE */
1284 // intercepted  case XF86XK_AudioLowerVolume: key_pressed = KPVOLD;     break;  /* VOL - */
1285                 case XF86XK_ScrollUp:   key_pressed = KPCHUP;   break;  /* CH + */
1286                 case XF86XK_ScrollDown: key_pressed = KPCHDN;   break;  /* CH - */
1287                 case XF86XK_AudioRecord: key_pressed = KPRECD;  break;  /* ( o) red */
1288                 case XF86XK_Forward:    key_pressed = KPPLAY;   break;  /* ( >) */
1289                 case XK_Redo:           key_pressed = KPFWRD;   break;  /* (>>) */
1290                 case XF86XK_Back:       key_pressed = KPBACK;   break;  /* (<<) */
1291                 case XK_Cancel:         key_pressed = KPSTOP;   break;  /* ([]) */
1292                 case XK_Pause:          key_pressed = KPAUSE;   break;  /* ('') */
1293
1294                 default:
1295                         key_pressed = keysym & 0xff;
1296 #ifdef X_HAVE_UTF8_STRING
1297 //printf("BC_WindowBase::dispatch_event %d\n", __LINE__);
1298                         keys_return[1] = 0;
1299 #endif
1300                         break;
1301                 }
1302 #ifdef X_HAVE_UTF8_STRING
1303                 }
1304                 key_pressed_utf8 = keys_return;
1305 #endif
1306
1307
1308                 result = 0;
1309                 if( top_level == this )
1310                         result = BC_KeyboardHandler::run_listeners(this);
1311
1312 //printf("BC_WindowBase::dispatch_event %d %d %x\n", shift_down(), alt_down(), key_pressed);
1313                 if( !result )
1314                         result = dispatch_keypress_event();
1315 // Handle some default keypresses
1316                 if(!result)
1317                 {
1318                         if(key_pressed == 'w' ||
1319                                 key_pressed == 'W')
1320                         {
1321                                 close_event();
1322                         }
1323                 }
1324                 break;
1325
1326         case KeyRelease:
1327                 XLookupString((XKeyEvent*)event, keys_return, 1, &keysym, 0);
1328                 dispatch_keyrelease_event();
1329 // printf("BC_WindowBase::dispatch_event KeyRelease keysym=0x%x keystate=0x%lld\n",
1330 // keysym, event->xkey.state);
1331                 break;
1332
1333         case LeaveNotify:
1334                 if( event->xcrossing.mode != NotifyNormal ) break;
1335                 cursor_entered = 0;
1336                 event_win = event->xany.window;
1337                 dispatch_cursor_leave();
1338                 break;
1339
1340         case EnterNotify:
1341                 if( event->xcrossing.mode != NotifyNormal ) break;
1342
1343                 if( !cursor_entered ) {
1344                         for( int i=0; i<popups.size(); ++i ) {  // popups always take focus
1345                                 if( popups[i]->win == event->xcrossing.window )
1346                                 cursor_entered = 1;
1347                         }
1348                         if( !cursor_entered && get_resources()->grab_input_focus &&
1349                             !event->xcrossing.focus && event->xcrossing.window == win ) {
1350                                 cursor_entered = 1;
1351                         }
1352                         if( cursor_entered )
1353                                 focus();
1354                 }
1355                 event_win = event->xany.window;
1356                 cursor_x = event->xcrossing.x;
1357                 cursor_y = event->xcrossing.y;
1358                 dispatch_cursor_enter();
1359                 break;
1360
1361         default:
1362                 break;
1363         }
1364 //printf("100 %s %p %d\n", title, event, event->type);
1365 //if(event->type != ClientMessage) dump();
1366
1367 #ifndef SINGLE_THREAD
1368         unlock_window();
1369         if(event) {
1370                 if( resend_event_window ) {
1371                         resend_event_window->put_event(event);
1372                         resend_event_window = 0;
1373                 }
1374                 else
1375                         delete event;
1376         }
1377 #else
1378 //      if(done) completion_lock->unlock();
1379 #endif
1380
1381 if(debug) printf("BC_WindowBase::dispatch_event this=%p %d\n", this, __LINE__);
1382         return 0;
1383 }
1384
1385 int BC_WindowBase::dispatch_expose_event()
1386 {
1387         int result = 0;
1388         for(int i = 0; i < subwindows->total && !result; i++)
1389         {
1390                 result = subwindows->values[i]->dispatch_expose_event();
1391         }
1392
1393 // Propagate to user
1394         if(!result) expose_event();
1395         return result;
1396 }
1397
1398 int BC_WindowBase::dispatch_resize_event(int w, int h)
1399 {
1400 // Can't store new w and h until the event is handles
1401 // because bcfilebox depends on the old w and h to
1402 // reposition widgets.
1403         if( window_type == MAIN_WINDOW ) {
1404                 flash_enabled = 0;
1405                 resize_events = 0;
1406
1407                 delete pixmap;
1408                 pixmap = new BC_Pixmap(this, w, h);
1409                 clear_box(0, 0, w, h);
1410         }
1411
1412 // Propagate to subwindows
1413         for(int i = 0; i < subwindows->total; i++) {
1414                 subwindows->values[i]->dispatch_resize_event(w, h);
1415         }
1416
1417 // Propagate to user
1418         resize_event(w, h);
1419
1420         if( window_type == MAIN_WINDOW ) {
1421                 this->w = w;
1422                 this->h = h;
1423                 dispatch_flash();
1424                 flush();
1425         }
1426         return 0;
1427 }
1428
1429 int BC_WindowBase::dispatch_flash()
1430 {
1431         flash_enabled = 1;
1432         for(int i = 0; i < subwindows->total; i++)
1433                 subwindows->values[i]->dispatch_flash();
1434         return flash(0);
1435 }
1436
1437 int BC_WindowBase::dispatch_translation_event()
1438 {
1439         translation_events = 0;
1440         if(window_type == MAIN_WINDOW)
1441         {
1442                 prev_x = x;
1443                 prev_y = y;
1444                 x = last_translate_x;
1445                 y = last_translate_y;
1446 // Correct for window manager offsets
1447                 x -= x_correction;
1448                 y -= y_correction;
1449         }
1450
1451         for(int i = 0; i < subwindows->total; i++)
1452         {
1453                 subwindows->values[i]->dispatch_translation_event();
1454         }
1455
1456         translation_event();
1457         return 0;
1458 }
1459
1460 int BC_WindowBase::dispatch_motion_event()
1461 {
1462         int result = 0;
1463         unhide_cursor();
1464
1465         if(top_level == this)
1466         {
1467                 motion_events = 0;
1468                 event_win = last_motion_win;
1469                 get_key_masks(last_motion_state);
1470
1471 // Test for grab
1472                 if(get_button_down() && !active_menubar && !active_popup_menu)
1473                 {
1474                         if(!result)
1475                         {
1476                                 cursor_x = last_motion_x;
1477                                 cursor_y = last_motion_y;
1478                                 result = dispatch_drag_motion();
1479                         }
1480
1481                         if(!result &&
1482                                 (last_motion_x < drag_x1 || last_motion_x >= drag_x2 ||
1483                                 last_motion_y < drag_y1 || last_motion_y >= drag_y2))
1484                         {
1485                                 cursor_x = drag_x;
1486                                 cursor_y = drag_y;
1487
1488                                 result = dispatch_drag_start();
1489                         }
1490                 }
1491
1492                 cursor_x = last_motion_x;
1493                 cursor_y = last_motion_y;
1494
1495 // printf("BC_WindowBase::dispatch_motion_event %d %p %p %p\n",
1496 // __LINE__,
1497 // active_menubar,
1498 // active_popup_menu,
1499 // active_subwindow);
1500
1501                 if(active_menubar && !result) result = active_menubar->dispatch_motion_event();
1502                 if(active_popup_menu && !result) result = active_popup_menu->dispatch_motion_event();
1503                 if(active_subwindow && !result) result = active_subwindow->dispatch_motion_event();
1504         }
1505
1506 // Dispatch in stacking order
1507         for(int i = subwindows->size() - 1; i >= 0 && !result; i--)
1508         {
1509                 result = subwindows->values[i]->dispatch_motion_event();
1510         }
1511
1512         if(!result) result = cursor_motion_event();    // give to user
1513         return result;
1514 }
1515
1516 int BC_WindowBase::dispatch_keypress_event()
1517 {
1518         int result = 0;
1519         if(top_level == this)
1520         {
1521                 if(active_subwindow) result = active_subwindow->dispatch_keypress_event();
1522         }
1523
1524         for(int i = 0; i < subwindows->total && !result; i++)
1525         {
1526                 result = subwindows->values[i]->dispatch_keypress_event();
1527         }
1528
1529         if(!result) result = keypress_event();
1530
1531         return result;
1532 }
1533
1534 int BC_WindowBase::dispatch_keyrelease_event()
1535 {
1536         int result = 0;
1537         if(top_level == this)
1538         {
1539                 if(active_subwindow) result = active_subwindow->dispatch_keyrelease_event();
1540         }
1541
1542         for(int i = 0; i < subwindows->total && !result; i++)
1543         {
1544                 result = subwindows->values[i]->dispatch_keyrelease_event();
1545         }
1546
1547         if(!result) result = keyrelease_event();
1548
1549         return result;
1550 }
1551
1552 int BC_WindowBase::dispatch_focus_in()
1553 {
1554         for(int i = 0; i < subwindows->total; i++)
1555         {
1556                 subwindows->values[i]->dispatch_focus_in();
1557         }
1558
1559         focus_in_event();
1560
1561         return 0;
1562 }
1563
1564 int BC_WindowBase::dispatch_focus_out()
1565 {
1566         for(int i = 0; i < subwindows->total; i++)
1567         {
1568                 subwindows->values[i]->dispatch_focus_out();
1569         }
1570
1571         focus_out_event();
1572
1573         return 0;
1574 }
1575
1576 int BC_WindowBase::get_has_focus()
1577 {
1578         return top_level->has_focus;
1579 }
1580
1581 int BC_WindowBase::get_deleting()
1582 {
1583         if(is_deleting) return 1;
1584         if(parent_window && parent_window->get_deleting()) return 1;
1585         return 0;
1586 }
1587
1588 int BC_WindowBase::dispatch_button_press()
1589 {
1590         int result = 0;
1591
1592
1593         if(top_level == this)
1594         {
1595                 if(active_menubar) result = active_menubar->dispatch_button_press();
1596                 if(active_popup_menu && !result) result = active_popup_menu->dispatch_button_press();
1597                 if(active_subwindow && !result) result = active_subwindow->dispatch_button_press();
1598         }
1599
1600         for(int i = 0; i < subwindows->total && !result; i++)
1601         {
1602                 result = subwindows->values[i]->dispatch_button_press();
1603         }
1604
1605         if(!result) result = button_press_event();
1606
1607
1608         return result;
1609 }
1610
1611 int BC_WindowBase::dispatch_button_release()
1612 {
1613         int result = 0;
1614         if(top_level == this)
1615         {
1616                 if(active_menubar) result = active_menubar->dispatch_button_release();
1617                 if(active_popup_menu && !result) result = active_popup_menu->dispatch_button_release();
1618                 if(active_subwindow && !result) result = active_subwindow->dispatch_button_release();
1619                 if(!result && button_number != 4 && button_number != 5)
1620                         result = dispatch_drag_stop();
1621         }
1622
1623         for(int i = 0; i < subwindows->total && !result; i++)
1624         {
1625                 result = subwindows->values[i]->dispatch_button_release();
1626         }
1627
1628         if(!result)
1629         {
1630                 result = button_release_event();
1631         }
1632
1633         return result;
1634 }
1635
1636
1637 int BC_WindowBase::dispatch_repeat_event(int64_t duration)
1638 {
1639
1640 // all repeat event handlers get called and decide based on activity and duration
1641 // whether to respond
1642         for(int i = 0; i < subwindows->total; i++)
1643         {
1644                 subwindows->values[i]->dispatch_repeat_event(duration);
1645         }
1646
1647
1648         repeat_event(duration);
1649
1650
1651
1652 // Unlock next repeat signal
1653         if(window_type == MAIN_WINDOW)
1654         {
1655 #ifdef SINGLE_THREAD
1656                 BC_Display::display_global->unlock_repeaters(duration);
1657 #else
1658                 for(int i = 0; i < repeaters.total; i++)
1659                 {
1660                         if(repeaters.values[i]->delay == duration)
1661                         {
1662                                 repeaters.values[i]->repeat_lock->unlock();
1663                         }
1664                 }
1665 #endif
1666         }
1667         return 0;
1668 }
1669
1670 void BC_WindowBase::unhide_cursor()
1671 {
1672         if(is_transparent)
1673         {
1674                 is_transparent = 0;
1675                 if(top_level->is_hourglass)
1676                         set_cursor(HOURGLASS_CURSOR, 1, 0);
1677                 else
1678                         set_cursor(current_cursor, 1, 0);
1679         }
1680         cursor_timer->update();
1681 }
1682
1683
1684 void BC_WindowBase::update_video_cursor()
1685 {
1686         if(video_on && !is_transparent)
1687         {
1688                 if(cursor_timer->get_difference() > VIDEO_CURSOR_TIMEOUT && !is_transparent)
1689                 {
1690                         is_transparent = 1;
1691                         set_cursor(TRANSPARENT_CURSOR, 1, 1);
1692                         cursor_timer->update();
1693                 }
1694         }
1695         else
1696         {
1697                 cursor_timer->update();
1698         }
1699 }
1700
1701
1702 int BC_WindowBase::dispatch_cursor_leave()
1703 {
1704         unhide_cursor();
1705
1706         for(int i = 0; i < subwindows->total; i++)
1707         {
1708                 subwindows->values[i]->dispatch_cursor_leave();
1709         }
1710
1711         cursor_leave_event();
1712         return 0;
1713 }
1714
1715 int BC_WindowBase::dispatch_cursor_enter()
1716 {
1717         int result = 0;
1718
1719         unhide_cursor();
1720
1721         if(active_menubar) result = active_menubar->dispatch_cursor_enter();
1722         if(!result && active_popup_menu) result = active_popup_menu->dispatch_cursor_enter();
1723         if(!result && active_subwindow) result = active_subwindow->dispatch_cursor_enter();
1724
1725         for(int i = 0; !result && i < subwindows->total; i++)
1726         {
1727                 result = subwindows->values[i]->dispatch_cursor_enter();
1728         }
1729
1730         if(!result) result = cursor_enter_event();
1731         return result;
1732 }
1733
1734 int BC_WindowBase::cursor_enter_event()
1735 {
1736         return 0;
1737 }
1738
1739 int BC_WindowBase::cursor_leave_event()
1740 {
1741         return 0;
1742 }
1743
1744 int BC_WindowBase::close_event()
1745 {
1746         set_done(1);
1747         return 1;
1748 }
1749
1750 int BC_WindowBase::dispatch_drag_start()
1751 {
1752         int result = 0;
1753         if(active_menubar) result = active_menubar->dispatch_drag_start();
1754         if(!result && active_popup_menu) result = active_popup_menu->dispatch_drag_start();
1755         if(!result && active_subwindow) result = active_subwindow->dispatch_drag_start();
1756
1757         for(int i = 0; i < subwindows->total && !result; i++)
1758         {
1759                 result = subwindows->values[i]->dispatch_drag_start();
1760         }
1761
1762         if(!result) result = is_dragging = drag_start_event();
1763         return result;
1764 }
1765
1766 int BC_WindowBase::dispatch_drag_stop()
1767 {
1768         int result = 0;
1769
1770         for(int i = 0; i < subwindows->total && !result; i++)
1771         {
1772                 result = subwindows->values[i]->dispatch_drag_stop();
1773         }
1774
1775         if(is_dragging && !result)
1776         {
1777                 drag_stop_event();
1778                 is_dragging = 0;
1779                 result = 1;
1780         }
1781
1782         return result;
1783 }
1784
1785 int BC_WindowBase::dispatch_drag_motion()
1786 {
1787         int result = 0;
1788         for(int i = 0; i < subwindows->total && !result; i++)
1789         {
1790                 result = subwindows->values[i]->dispatch_drag_motion();
1791         }
1792
1793         if(is_dragging && !result)
1794         {
1795                 drag_motion_event();
1796                 result = 1;
1797         }
1798
1799         return result;
1800 }
1801
1802
1803 int BC_WindowBase::show_tooltip(const char *text, int x, int y, int w, int h)
1804 {
1805 // default text
1806         int forced = !text ? force_tooltip : 1;
1807         if( !text ) text = tooltip_text;
1808         if( !text || (!forced && !get_resources()->tooltips_enabled) ) {
1809                 top_level->hide_tooltip();
1810                 return 1;
1811         }
1812 // default w,h
1813         if(w < 0) w = get_text_width(MEDIUMFONT, text)  + TOOLTIP_MARGIN * 2;
1814         if(h < 0) h = get_text_height(MEDIUMFONT, text) + TOOLTIP_MARGIN * 2;
1815 // default x,y (win relative)
1816         if( x < 0 ) x = get_w();
1817         if( y < 0 ) y = get_h();
1818         int wx, wy;
1819         get_root_coordinates(x, y, &wx, &wy);
1820 // keep the tip inside the window/display
1821         int x0 = top_level->get_x(), x1 = x0 + top_level->get_w();
1822         int x2 = top_level->get_screen_x(0, -1) + top_level->get_screen_w(0, -1);
1823         if( x1 > x2 ) x1 = x2;
1824         if( wx < x0 ) wx = x0;
1825         if( wx >= (x1-=w) ) wx = x1;
1826         int y0 = top_level->get_y(), y1 = y0 + top_level->get_h();
1827         int y2 = top_level->get_root_h(0);
1828         if( y1 > y2 ) y1 = y2;
1829         if( wy < y0 ) wy = y0;
1830         if( wy >= (y1-=h) ) wy = y1;
1831 // avoid tip under cursor (flickers)
1832         int abs_x, abs_y;
1833         get_abs_cursor(abs_x,abs_y, 0);
1834         if( wx < abs_x && abs_x < wx+w && wy < abs_y && abs_y < wy+h ) {
1835                 if( wx-abs_x < wy-abs_y )
1836                         wx = abs_x+1;
1837                 else
1838                         wy = abs_y+1;
1839         }
1840         if( !tooltip_on ) {
1841                 tooltip_on = 1;
1842                 tooltip_popup = new BC_Popup(top_level, wx, wy, w, h,
1843                                 get_resources()->tooltip_bg_color);
1844         }
1845         else
1846                 tooltip_popup->reposition_window(wx, wy, w, h);
1847
1848         draw_tooltip(text);
1849         tooltip_popup->flash();
1850         tooltip_popup->flush();
1851         return 0;
1852 }
1853
1854 int BC_WindowBase::hide_tooltip()
1855 {
1856         if(subwindows)
1857                 for(int i = 0; i < subwindows->total; i++)
1858                 {
1859                         subwindows->values[i]->hide_tooltip();
1860                 }
1861
1862         if(tooltip_on)
1863         {
1864                 tooltip_on = 0;
1865                 delete tooltip_popup;
1866                 tooltip_popup = 0;
1867         }
1868         return 0;
1869 }
1870
1871 const char *BC_WindowBase::get_tooltip()
1872 {
1873         return tooltip_text;
1874 }
1875
1876 int BC_WindowBase::set_tooltip(const char *text)
1877 {
1878         tooltip_text = text;
1879
1880 // Update existing tooltip if it is visible
1881         if(tooltip_on)
1882         {
1883                 draw_tooltip();
1884                 tooltip_popup->flash();
1885         }
1886         return 0;
1887 }
1888 // signal the event handler to repeat
1889 int BC_WindowBase::set_repeat(int64_t duration)
1890 {
1891         if(duration <= 0)
1892         {
1893                 printf("BC_WindowBase::set_repeat duration=%jd\n", duration);
1894                 return 0;
1895         }
1896         if(window_type != MAIN_WINDOW) return top_level->set_repeat(duration);
1897
1898 #ifdef SINGLE_THREAD
1899         BC_Display::display_global->set_repeat(this, duration);
1900 #else
1901 // test repeater database for duplicates
1902         for(int i = 0; i < repeaters.total; i++)
1903         {
1904 // Already exists
1905                 if(repeaters.values[i]->delay == duration)
1906                 {
1907                         repeaters.values[i]->start_repeating(this);
1908                         return 0;
1909                 }
1910         }
1911
1912         BC_Repeater *repeater = new BC_Repeater(this, duration);
1913         repeater->initialize();
1914         repeaters.append(repeater);
1915         repeater->start_repeating();
1916 #endif
1917         return 0;
1918 }
1919
1920 int BC_WindowBase::unset_repeat(int64_t duration)
1921 {
1922         if(window_type != MAIN_WINDOW) return top_level->unset_repeat(duration);
1923
1924 #ifdef SINGLE_THREAD
1925         BC_Display::display_global->unset_repeat(this, duration);
1926 #else
1927         for(int i = 0; i < repeaters.total; i++)
1928         {
1929                 if(repeaters.values[i]->delay == duration)
1930                 {
1931                         repeaters.values[i]->stop_repeating();
1932                 }
1933         }
1934 #endif
1935         return 0;
1936 }
1937
1938
1939 int BC_WindowBase::unset_all_repeaters()
1940 {
1941 #ifdef SINGLE_THREAD
1942         BC_Display::display_global->unset_all_repeaters(this);
1943 #else
1944         for(int i = 0; i < repeaters.total; i++)
1945         {
1946                 repeaters.values[i]->stop_repeating();
1947         }
1948         repeaters.remove_all_objects();
1949 #endif
1950         return 0;
1951 }
1952
1953 // long BC_WindowBase::get_repeat_id()
1954 // {
1955 //      return top_level->next_repeat_id++;
1956 // }
1957
1958 XEvent *BC_WindowBase::new_xevent()
1959 {
1960         XEvent *event = new XEvent;
1961         memset(event, 0, sizeof(*event));
1962         return event;
1963 }
1964
1965 #ifndef SINGLE_THREAD
1966 int BC_WindowBase::arm_repeat(int64_t duration)
1967 {
1968         XEvent *event = new_xevent();
1969         XClientMessageEvent *ptr = (XClientMessageEvent*)event;
1970         ptr->type = ClientMessage;
1971         ptr->message_type = RepeaterXAtom;
1972         ptr->format = 32;
1973         ptr->data.l[0] = duration;
1974
1975 // Couldn't use XSendEvent since it locked up randomly.
1976         put_event(event);
1977         return 0;
1978 }
1979 #endif
1980
1981 int BC_WindowBase::receive_custom_xatoms(xatom_event *event)
1982 {
1983         return 0;
1984 }
1985
1986 int BC_WindowBase::send_custom_xatom(xatom_event *event)
1987 {
1988 #ifndef SINGLE_THREAD
1989         XEvent *myevent = new_xevent();
1990         XClientMessageEvent *ptr = (XClientMessageEvent*)myevent;
1991         ptr->type = ClientMessage;
1992         ptr->message_type = event->message_type;
1993         ptr->format = event->format;
1994         ptr->data.l[0] = event->data.l[0];
1995         ptr->data.l[1] = event->data.l[1];
1996         ptr->data.l[2] = event->data.l[2];
1997         ptr->data.l[3] = event->data.l[3];
1998         ptr->data.l[4] = event->data.l[4];
1999
2000         put_event(myevent);
2001 #endif
2002         return 0;
2003 }
2004
2005
2006
2007 Atom BC_WindowBase::create_xatom(const char *atom_name)
2008 {
2009         return XInternAtom(display, atom_name, False);
2010 }
2011
2012 int BC_WindowBase::get_atoms()
2013 {
2014         SetDoneXAtom =  XInternAtom(display, "BC_REPEAT_EVENT", False);
2015         RepeaterXAtom = XInternAtom(display, "BC_CLOSE_EVENT", False);
2016         DestroyAtom =   XInternAtom(display, "BC_DESTROY_WINDOW", False);
2017         DelWinXAtom =   XInternAtom(display, "WM_DELETE_WINDOW", False);
2018         if( (ProtoXAtom = XInternAtom(display, "WM_PROTOCOLS", False)) != 0 )
2019                 XChangeProperty(display, win, ProtoXAtom, XA_ATOM, 32,
2020                         PropModeReplace, (unsigned char *)&DelWinXAtom, True);
2021         return 0;
2022
2023 }
2024
2025
2026 void BC_WindowBase::init_cursors()
2027 {
2028         arrow_cursor = XCreateFontCursor(display, XC_top_left_arrow);
2029         cross_cursor = XCreateFontCursor(display, XC_crosshair);
2030         ibeam_cursor = XCreateFontCursor(display, XC_xterm);
2031         vseparate_cursor = XCreateFontCursor(display, XC_sb_v_double_arrow);
2032         hseparate_cursor = XCreateFontCursor(display, XC_sb_h_double_arrow);
2033         move_cursor = XCreateFontCursor(display, XC_fleur);
2034         left_cursor = XCreateFontCursor(display, XC_sb_left_arrow);
2035         right_cursor = XCreateFontCursor(display, XC_sb_right_arrow);
2036         upright_arrow_cursor = XCreateFontCursor(display, XC_arrow);
2037         upleft_resize_cursor = XCreateFontCursor(display, XC_top_left_corner);
2038         upright_resize_cursor = XCreateFontCursor(display, XC_top_right_corner);
2039         downleft_resize_cursor = XCreateFontCursor(display, XC_bottom_left_corner);
2040         downright_resize_cursor = XCreateFontCursor(display, XC_bottom_right_corner);
2041         hourglass_cursor = XCreateFontCursor(display, XC_watch);
2042         grabbed_cursor = create_grab_cursor();
2043
2044         static char cursor_data[] = { 0,0,0,0, 0,0,0,0 };
2045         Colormap colormap = DefaultColormap(display, screen);
2046         Pixmap pixmap_bottom = XCreateBitmapFromData(display,
2047                 rootwin, cursor_data, 8, 8);
2048         XColor black, dummy;
2049         XAllocNamedColor(display, colormap, "black", &black, &dummy);
2050         transparent_cursor = XCreatePixmapCursor(display,
2051                 pixmap_bottom, pixmap_bottom, &black, &black, 0, 0);
2052 //      XDefineCursor(display, win, transparent_cursor);
2053         XFreePixmap(display, pixmap_bottom);
2054 }
2055
2056 int BC_WindowBase::evaluate_color_model(int client_byte_order, int server_byte_order, int depth)
2057 {
2058         int color_model = BC_TRANSPARENCY;
2059         switch(depth)
2060         {
2061                 case 8:
2062                         color_model = BC_RGB8;
2063                         break;
2064                 case 16:
2065                         color_model = (server_byte_order == client_byte_order) ? BC_RGB565 : BC_BGR565;
2066                         break;
2067                 case 24:
2068                         color_model = server_byte_order ? BC_BGR888 : BC_RGB888;
2069                         break;
2070                 case 32:
2071                         color_model = server_byte_order ? BC_BGR8888 : BC_ARGB8888;
2072                         break;
2073         }
2074         return color_model;
2075 }
2076
2077 int BC_WindowBase::init_colors()
2078 {
2079         total_colors = 0;
2080         current_color_value = current_color_pixel = 0;
2081
2082 // Get the real depth
2083         char *data = 0;
2084         XImage *ximage;
2085         ximage = XCreateImage(top_level->display,
2086                         top_level->vis, top_level->default_depth,
2087                         ZPixmap, 0, data, 16, 16, 8, 0);
2088         bits_per_pixel = ximage->bits_per_pixel;
2089         XDestroyImage(ximage);
2090
2091         color_model = evaluate_color_model(client_byte_order,
2092                 server_byte_order,
2093                 bits_per_pixel);
2094 // Get the color model
2095         switch(color_model)
2096         {
2097                 case BC_RGB8:
2098                         if(private_color) {
2099                                 cmap = XCreateColormap(display, rootwin, vis, AllocNone);
2100                                 create_private_colors();
2101                         }
2102                         else {
2103                                 cmap = DefaultColormap(display, screen);
2104                                 create_shared_colors();
2105                         }
2106
2107                         allocate_color_table();
2108                         break;
2109
2110                 default:
2111                         //cmap = DefaultColormap(display, screen);
2112                         cmap = XCreateColormap(display, rootwin, vis, AllocNone );
2113                         break;
2114         }
2115         return 0;
2116 }
2117
2118 int BC_WindowBase::create_private_colors()
2119 {
2120         int color;
2121         total_colors = 256;
2122
2123         for(int i = 0; i < 255; i++)
2124         {
2125                 color = (i & 0xc0) << 16;
2126                 color += (i & 0x38) << 10;
2127                 color += (i & 0x7) << 5;
2128                 color_table[i][0] = color;
2129         }
2130         create_shared_colors();        // overwrite the necessary colors on the table
2131         return 0;
2132 }
2133
2134
2135 int BC_WindowBase::create_color(int color)
2136 {
2137         if(total_colors == 256)
2138         {
2139 // replace the closest match with an exact match
2140                 color_table[get_color_rgb8(color)][0] = color;
2141         }
2142         else
2143         {
2144 // add the color to the table
2145                 color_table[total_colors][0] = color;
2146                 total_colors++;
2147         }
2148         return 0;
2149 }
2150
2151 int BC_WindowBase::create_shared_colors()
2152 {
2153         create_color(BLACK);
2154         create_color(WHITE);
2155
2156         create_color(LTGREY);
2157         create_color(MEGREY);
2158         create_color(MDGREY);
2159         create_color(DKGREY);
2160
2161         create_color(LTCYAN);
2162         create_color(MECYAN);
2163         create_color(MDCYAN);
2164         create_color(DKCYAN);
2165
2166         create_color(LTGREEN);
2167         create_color(GREEN);
2168         create_color(DKGREEN);
2169
2170         create_color(LTPINK);
2171         create_color(PINK);
2172         create_color(RED);
2173
2174         create_color(LTBLUE);
2175         create_color(BLUE);
2176         create_color(DKBLUE);
2177
2178         create_color(LTYELLOW);
2179         create_color(MEYELLOW);
2180         create_color(MDYELLOW);
2181         create_color(DKYELLOW);
2182
2183         create_color(LTPURPLE);
2184         create_color(MEPURPLE);
2185         create_color(MDPURPLE);
2186         create_color(DKPURPLE);
2187
2188         create_color(FGGREY);
2189         create_color(MNBLUE);
2190         create_color(ORANGE);
2191         create_color(FTGREY);
2192
2193         return 0;
2194 }
2195
2196 Cursor BC_WindowBase::create_grab_cursor()
2197 {
2198         int iw = 23, iw1 = iw-1, iw2 = iw/2;
2199         int ih = 23, ih1 = ih-1, ih2 = ih/2;
2200         VFrame grab(iw,ih,BC_RGB888);
2201         grab.clear_frame();
2202         grab.set_pixel_color(RED);   // fg
2203         grab.draw_smooth(iw2,0,   iw1,0,   iw1,ih2);
2204         grab.draw_smooth(iw1,ih2, iw1,ih1, iw2,ih1);
2205         grab.draw_smooth(iw2,ih1, 0,ih1,   0,ih2);
2206         grab.draw_smooth(0,ih2,   0,0,     iw2,0);
2207         grab.set_pixel_color(WHITE); // bg
2208         grab.draw_line(0,ih2,     iw2-2,ih2);
2209         grab.draw_line(iw2+2,ih2, iw1,ih2);
2210         grab.draw_line(iw2,0,     iw2,ih2-2);
2211         grab.draw_line(iw2,ih2+2, iw2,ih1);
2212
2213         int bpl = (iw+7)/8, isz = bpl * ih;
2214         char img[isz];  memset(img, 0, isz);
2215         char msk[isz];  memset(msk, 0, isz);
2216         unsigned char **rows = grab.get_rows();
2217         for( int iy=0; iy<ih; ++iy ) {
2218                 char *op = img + iy*bpl;
2219                 char *mp = msk + iy*bpl;
2220                 unsigned char *ip = rows[iy];
2221                 for( int ix=0; ix<iw; ++ix,ip+=3 ) {
2222                         if( ip[0] ) mp[ix>>3] |= (1<<(ix&7));
2223                         if( !ip[1] ) op[ix>>3] |= (1<<(ix&7));
2224                 }
2225         }
2226         unsigned long white_pix = WhitePixel(display, screen);
2227         unsigned long black_pix = BlackPixel(display, screen);
2228         Pixmap img_xpm = XCreatePixmapFromBitmapData(display, rootwin,
2229                 img, iw,ih, white_pix,black_pix, 1);
2230         Pixmap msk_xpm = XCreatePixmapFromBitmapData(display, rootwin,
2231                 msk, iw,ih, white_pix,black_pix, 1);
2232
2233         XColor fc, bc;
2234         fc.flags = bc.flags = DoRed | DoGreen | DoBlue;
2235         fc.red = 0xffff; fc.green = fc.blue = 0;  // fg
2236         bc.red = 0xffff; bc.green = 0xffff; bc.blue = 0x0000;     // bg
2237         Cursor cursor = XCreatePixmapCursor(display, img_xpm,msk_xpm, &fc,&bc, iw2,ih2);
2238         XFreePixmap(display, img_xpm);
2239         XFreePixmap(display, msk_xpm);
2240         return cursor;
2241 }
2242
2243 int BC_WindowBase::allocate_color_table()
2244 {
2245         int red, green, blue, color;
2246         XColor col;
2247
2248         for(int i = 0; i < total_colors; i++)
2249         {
2250                 color = color_table[i][0];
2251                 red = (color & 0xFF0000) >> 16;
2252                 green = (color & 0x00FF00) >> 8;
2253                 blue = color & 0xFF;
2254
2255                 col.flags = DoRed | DoGreen | DoBlue;
2256                 col.red   = red<<8   | red;
2257                 col.green = green<<8 | green;
2258                 col.blue  = blue<<8  | blue;
2259
2260                 XAllocColor(display, cmap, &col);
2261                 color_table[i][1] = col.pixel;
2262         }
2263
2264         XInstallColormap(display, cmap);
2265         return 0;
2266 }
2267
2268 int BC_WindowBase::init_window_shape()
2269 {
2270         if(bg_pixmap && bg_pixmap->use_alpha())
2271         {
2272                 XShapeCombineMask(top_level->display,
2273                         this->win, ShapeBounding, 0, 0,
2274                         bg_pixmap->get_alpha(), ShapeSet);
2275         }
2276         return 0;
2277 }
2278
2279
2280 int BC_WindowBase::init_gc()
2281 {
2282         unsigned long gcmask;
2283         gcmask = GCFont | GCGraphicsExposures;
2284
2285         XGCValues gcvalues;
2286         gcvalues.font = mediumfont->fid;        // set the font
2287         gcvalues.graphics_exposures = 0;        // prevent expose events for every redraw
2288         gc = XCreateGC(display, rootwin, gcmask, &gcvalues);
2289
2290 // gcmask = GCCapStyle | GCJoinStyle;
2291 // XGetGCValues(display, gc, gcmask, &gcvalues);
2292 // printf("BC_WindowBase::init_gc %d %d %d\n", __LINE__, gcvalues.cap_style, gcvalues.join_style);
2293         return 0;
2294 }
2295
2296 int BC_WindowBase::init_fonts()
2297 {
2298         if( !(smallfont = XLoadQueryFont(display, _(resources->small_font))) )
2299                 if( !(smallfont = XLoadQueryFont(display, _(resources->small_font2))) )
2300                         smallfont = XLoadQueryFont(display, "fixed");
2301         if( !(mediumfont = XLoadQueryFont(display, _(resources->medium_font))) )
2302                 if( !(mediumfont = XLoadQueryFont(display, _(resources->medium_font2))) )
2303                         mediumfont = XLoadQueryFont(display, "fixed");
2304         if( !(largefont = XLoadQueryFont(display, _(resources->large_font))) )
2305                 if( !(largefont = XLoadQueryFont(display, _(resources->large_font2))) )
2306                         largefont = XLoadQueryFont(display, "fixed");
2307         if( !(bigfont = XLoadQueryFont(display, _(resources->big_font))) )
2308                 if( !(bigfont = XLoadQueryFont(display, _(resources->big_font2))) )
2309                         bigfont = XLoadQueryFont(display, "fixed");
2310
2311         if((clockfont = XLoadQueryFont(display, _(resources->clock_font))) == NULL)
2312                 if((clockfont = XLoadQueryFont(display, _(resources->clock_font2))) == NULL)
2313                         clockfont = XLoadQueryFont(display, "fixed");
2314
2315         init_xft();
2316         if(get_resources()->use_fontset)
2317         {
2318                 char **m, *d;
2319                 int n;
2320
2321 // FIXME: should check the m,d,n values
2322                 smallfontset = XCreateFontSet(display, resources->small_fontset, &m, &n, &d);
2323                 if( !smallfontset )
2324                         smallfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2325                 mediumfontset = XCreateFontSet(display, resources->medium_fontset, &m, &n, &d);
2326                 if( !mediumfontset )
2327                         mediumfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2328                 largefontset = XCreateFontSet(display, resources->large_fontset, &m, &n, &d);
2329                 if( !largefontset )
2330                         largefontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2331                 bigfontset = XCreateFontSet(display, resources->big_fontset, &m, &n, &d);
2332                 if( !bigfontset )
2333                         bigfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2334                 clockfontset = XCreateFontSet(display, resources->clock_fontset, &m, &n, &d);
2335                 if( !clockfontset )
2336                         clockfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2337                 if(clockfontset && bigfontset && largefontset && mediumfontset && smallfontset) {
2338                         curr_fontset = mediumfontset;
2339                         get_resources()->use_fontset = 1;
2340                 }
2341                 else {
2342                         curr_fontset = 0;
2343                         get_resources()->use_fontset = 0;
2344                 }
2345         }
2346
2347         return 0;
2348 }
2349
2350 void BC_WindowBase::init_xft()
2351 {
2352 #ifdef HAVE_XFT
2353         if( !get_resources()->use_xft ) return;
2354 // apparently, xft is not reentrant, more than this is needed
2355 static Mutex xft_init_lock("BC_WindowBase::xft_init_lock", 0);
2356 xft_init_lock.lock("BC_WindowBase::init_xft");
2357         if(!(smallfont_xft =
2358                 (resources->small_font_xft[0] == '-' ?
2359                         xftFontOpenXlfd(display, screen, resources->small_font_xft) :
2360                         xftFontOpenName(display, screen, resources->small_font_xft))) )
2361                 if(!(smallfont_xft =
2362                         xftFontOpenXlfd(display, screen, resources->small_font_xft2)))
2363                         smallfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2364         if(!(mediumfont_xft =
2365                 (resources->medium_font_xft[0] == '-' ?
2366                         xftFontOpenXlfd(display, screen, resources->medium_font_xft) :
2367                         xftFontOpenName(display, screen, resources->medium_font_xft))) )
2368                 if(!(mediumfont_xft =
2369                         xftFontOpenXlfd(display, screen, resources->medium_font_xft2)))
2370                         mediumfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2371         if(!(largefont_xft =
2372                 (resources->large_font_xft[0] == '-' ?
2373                         xftFontOpenXlfd(display, screen, resources->large_font_xft) :
2374                         xftFontOpenName(display, screen, resources->large_font_xft))) )
2375                 if(!(largefont_xft =
2376                         xftFontOpenXlfd(display, screen, resources->large_font_xft2)))
2377                         largefont_xft = xftFontOpenXlfd(display, screen, "fixed");
2378         if(!(bigfont_xft =
2379                 (resources->big_font_xft[0] == '-' ?
2380                         xftFontOpenXlfd(display, screen, resources->big_font_xft) :
2381                         xftFontOpenName(display, screen, resources->big_font_xft))) )
2382                 if(!(bigfont_xft =
2383                         xftFontOpenXlfd(display, screen, resources->big_font_xft2)))
2384                         bigfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2385         if(!(clockfont_xft =
2386                 (resources->clock_font_xft[0] == '-' ?
2387                         xftFontOpenXlfd(display, screen, resources->clock_font_xft) :
2388                         xftFontOpenName(display, screen, resources->clock_font_xft))) )
2389                 clockfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2390
2391
2392         if(!(bold_smallfont_xft =
2393                 (resources->small_b_font_xft[0] == '-' ?
2394                         xftFontOpenXlfd(display, screen, resources->small_b_font_xft) :
2395                         xftFontOpenName(display, screen, resources->small_b_font_xft))) )
2396                 bold_smallfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2397         if(!(bold_mediumfont_xft =
2398                 (resources->medium_b_font_xft[0] == '-' ?
2399                         xftFontOpenXlfd(display, screen, resources->medium_b_font_xft) :
2400                         xftFontOpenName(display, screen, resources->medium_b_font_xft))) )
2401                 bold_mediumfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2402         if(!(bold_largefont_xft =
2403                 (resources->large_b_font_xft[0] == '-' ?
2404                         xftFontOpenXlfd(display, screen, resources->large_b_font_xft) :
2405                         xftFontOpenName(display, screen, resources->large_b_font_xft))) )
2406                 bold_largefont_xft = xftFontOpenXlfd(display, screen, "fixed");
2407
2408         if( !smallfont_xft || !mediumfont_xft || !largefont_xft || !bigfont_xft ||
2409             !bold_largefont_xft || !bold_mediumfont_xft || !bold_largefont_xft ||
2410             !clockfont_xft ) {
2411                 printf("BC_WindowBase::init_fonts: no xft fonts found:"
2412                         " %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n",
2413                         resources->small_font_xft, smallfont_xft,
2414                         resources->medium_font_xft, mediumfont_xft,
2415                         resources->large_font_xft, largefont_xft,
2416                         resources->big_font_xft, bigfont_xft,
2417                         resources->clock_font_xft, clockfont_xft,
2418                         resources->small_b_font_xft, bold_smallfont_xft,
2419                         resources->medium_b_font_xft, bold_mediumfont_xft,
2420                         resources->large_b_font_xft, bold_largefont_xft);
2421                 get_resources()->use_xft = 0;
2422                 exit(1);
2423         }
2424 // _XftDisplayInfo needs a lock.
2425         xftDefaultHasRender(display);
2426 xft_init_lock.unlock();
2427 #endif // HAVE_XFT
2428 }
2429
2430 void BC_WindowBase::init_glyphs()
2431 {
2432 // draw all ascii char glyphs
2433 //  There are problems with some/my graphics boards/drivers
2434 //  which cause some glyphs to be munged if draws occur while
2435 //  the font is being loaded.  This code fills the font caches
2436 //  by drawing all the ascii glyphs before the system starts.
2437 //  Not a fix, but much better than nothing.
2438         static int inited = 0;
2439         if( inited ) return;
2440         inited = 1;
2441         int cur_font = current_font;
2442 // locale encodings, needed glyphs to be preloaded
2443         const char *text = _( // ascii 0x20...0x7e
2444                 " !\"#$%&'()*+,-./0123456789:;<=>?"
2445                 "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
2446                 "`abcdefghijklmnopqrstuvwxyz{|}~");
2447         for( int font=SMALLFONT; font<=LARGEFONT; ++font ) {
2448                 set_font(font);
2449                 draw_text(5,5, text, 0);
2450         }
2451         set_font(cur_font);
2452 }
2453
2454 void BC_WindowBase::init_im()
2455 {
2456         XIMStyles *xim_styles;
2457         XIMStyle xim_style;
2458
2459         if(!(input_method = XOpenIM(display, NULL, NULL, NULL)))
2460         {
2461                 printf("BC_WindowBase::init_im: Could not open input method.\n");
2462                 exit(1);
2463         }
2464         if(XGetIMValues(input_method, XNQueryInputStyle, &xim_styles, NULL) ||
2465                         xim_styles == NULL)
2466         {
2467                 printf("BC_WindowBase::init_im: Input method doesn't support any styles.\n");
2468                 XCloseIM(input_method);
2469                 exit(1);
2470         }
2471
2472         xim_style = 0;
2473         for(int z = 0;  z < xim_styles->count_styles;  z++)
2474         {
2475                 if(xim_styles->supported_styles[z] == (XIMPreeditNothing | XIMStatusNothing))
2476                 {
2477                         xim_style = xim_styles->supported_styles[z];
2478                         break;
2479                 }
2480         }
2481         XFree(xim_styles);
2482
2483         if(xim_style == 0)
2484         {
2485                 printf("BC_WindowBase::init_im: Input method doesn't support the style we need.\n");
2486                 XCloseIM(input_method);
2487                 exit(1);
2488         }
2489
2490         input_context = XCreateIC(input_method, XNInputStyle, xim_style,
2491                 XNClientWindow, win, XNFocusWindow, win, NULL);
2492         if(!input_context)
2493         {
2494                 printf("BC_WindowBase::init_im: Failed to create input context.\n");
2495                 XCloseIM(input_method);
2496                 exit(1);
2497         }
2498 }
2499
2500 void BC_WindowBase::finit_im()
2501 {
2502         if( input_context ) {
2503                 XDestroyIC(input_context);
2504                 input_context = 0;
2505         }
2506         if( input_method ) {
2507                 XCloseIM(input_method);
2508                 input_method = 0;
2509         }
2510 }
2511
2512
2513 int BC_WindowBase::get_color(int64_t color)
2514 {
2515 // return pixel of color
2516 // use this only for drawing subwindows not for bitmaps
2517          int i, test, difference;
2518
2519         switch(color_model)
2520         {
2521         case BC_RGB8:
2522                 if(private_color)
2523                         return get_color_rgb8(color);
2524 // test last color looked up
2525                 if(current_color_value == color)
2526                         return current_color_pixel;
2527
2528 // look up in table
2529                 current_color_value = color;
2530                 for(i = 0; i < total_colors; i++)
2531                 {
2532                         if(color_table[i][0] == color)
2533                         {
2534                                 current_color_pixel = color_table[i][1];
2535                                 return current_color_pixel;
2536                         }
2537                 }
2538
2539 // find nearest match
2540                 difference = 0xFFFFFF;
2541
2542                 for(i = 0; i < total_colors; i++)
2543                 {
2544                         test = abs((int)(color_table[i][0] - color));
2545
2546                         if(test < difference)
2547                         {
2548                                 current_color_pixel = color_table[i][1];
2549                                 difference = test;
2550                         }
2551                 }
2552                 return current_color_pixel;
2553
2554         case BC_RGB565:
2555                 return get_color_rgb16(color);
2556
2557         case BC_BGR565:
2558                 return get_color_bgr16(color);
2559
2560         case BC_RGB888:
2561         case BC_BGR888:
2562                 return client_byte_order == server_byte_order ?
2563                         color : get_color_bgr24(color);
2564
2565         default:
2566                 return color;
2567         }
2568         return 0;
2569 }
2570
2571 int BC_WindowBase::get_color_rgb8(int color)
2572 {
2573         int pixel;
2574
2575         pixel = (color & 0xc00000) >> 16;
2576         pixel += (color & 0xe000) >> 10;
2577         pixel += (color & 0xe0) >> 5;
2578         return pixel;
2579 }
2580
2581 int64_t BC_WindowBase::get_color_rgb16(int color)
2582 {
2583         int64_t result;
2584         result = (color & 0xf80000) >> 8;
2585         result += (color & 0xfc00) >> 5;
2586         result += (color & 0xf8) >> 3;
2587
2588         return result;
2589 }
2590
2591 int64_t BC_WindowBase::get_color_bgr16(int color)
2592 {
2593         int64_t result;
2594         result = (color & 0xf80000) >> 19;
2595         result += (color & 0xfc00) >> 5;
2596         result += (color & 0xf8) << 8;
2597
2598         return result;
2599 }
2600
2601 int64_t BC_WindowBase::get_color_bgr24(int color)
2602 {
2603         int64_t result;
2604         result = (color & 0xff) << 16;
2605         result += (color & 0xff00);
2606         result += (color & 0xff0000) >> 16;
2607         return result;
2608 }
2609
2610 void BC_WindowBase::start_video()
2611 {
2612         cursor_timer->update();
2613         video_on = 1;
2614 //      set_color(BLACK);
2615 //      draw_box(0, 0, get_w(), get_h());
2616 //      flash();
2617 }
2618
2619 void BC_WindowBase::stop_video()
2620 {
2621         video_on = 0;
2622         unhide_cursor();
2623 }
2624
2625
2626
2627 int64_t BC_WindowBase::get_color()
2628 {
2629         return top_level->current_color;
2630 }
2631
2632 void BC_WindowBase::set_color(int64_t color)
2633 {
2634         top_level->current_color = color;
2635         XSetForeground(top_level->display,
2636                 top_level->gc,
2637                 top_level->get_color(color));
2638 }
2639
2640 void BC_WindowBase::set_opaque()
2641 {
2642         XSetFunction(top_level->display, top_level->gc, GXcopy);
2643 }
2644
2645 void BC_WindowBase::set_inverse()
2646 {
2647         XSetFunction(top_level->display, top_level->gc, GXxor);
2648 }
2649
2650 void BC_WindowBase::set_line_width(int value)
2651 {
2652         this->line_width = value;
2653         XSetLineAttributes(top_level->display, top_level->gc, value,    /* line_width */
2654                 line_dashes == 0 ? LineSolid : LineOnOffDash,           /* line_style */
2655                 line_dashes == 0 ? CapRound : CapNotLast,               /* cap_style */
2656                 JoinMiter);                                             /* join_style */
2657
2658         if(line_dashes > 0) {
2659                 const char dashes = line_dashes;
2660                 XSetDashes(top_level->display, top_level->gc, 0, &dashes, 1);
2661         }
2662
2663 // XGCValues gcvalues;
2664 // unsigned long gcmask;
2665 // gcmask = GCCapStyle | GCJoinStyle;
2666 // XGetGCValues(top_level->display, top_level->gc, gcmask, &gcvalues);
2667 // printf("BC_WindowBase::set_line_width %d %d %d\n", __LINE__, gcvalues.cap_style, gcvalues.join_style);
2668 }
2669
2670 void BC_WindowBase::set_line_dashes(int value)
2671 {
2672         line_dashes = value;
2673 // call XSetLineAttributes
2674         set_line_width(line_width);
2675 }
2676
2677
2678 Cursor BC_WindowBase::get_cursor_struct(int cursor)
2679 {
2680         switch(cursor)
2681         {
2682                 case ARROW_CURSOR:         return top_level->arrow_cursor;
2683                 case CROSS_CURSOR:         return top_level->cross_cursor;
2684                 case IBEAM_CURSOR:         return top_level->ibeam_cursor;
2685                 case VSEPARATE_CURSOR:     return top_level->vseparate_cursor;
2686                 case HSEPARATE_CURSOR:     return top_level->hseparate_cursor;
2687                 case MOVE_CURSOR:          return top_level->move_cursor;
2688                 case LEFT_CURSOR:          return top_level->left_cursor;
2689                 case RIGHT_CURSOR:         return top_level->right_cursor;
2690                 case UPRIGHT_ARROW_CURSOR: return top_level->upright_arrow_cursor;
2691                 case UPLEFT_RESIZE:        return top_level->upleft_resize_cursor;
2692                 case UPRIGHT_RESIZE:       return top_level->upright_resize_cursor;
2693                 case DOWNLEFT_RESIZE:      return top_level->downleft_resize_cursor;
2694                 case DOWNRIGHT_RESIZE:     return top_level->downright_resize_cursor;
2695                 case HOURGLASS_CURSOR:     return top_level->hourglass_cursor;
2696                 case TRANSPARENT_CURSOR:   return top_level->transparent_cursor;
2697                 case GRABBED_CURSOR:       return top_level->grabbed_cursor;
2698         }
2699         return 0;
2700 }
2701
2702 void BC_WindowBase::set_cursor(int cursor, int override, int flush)
2703 {
2704 // inherit cursor from parent
2705         if(cursor < 0)
2706         {
2707                 XUndefineCursor(top_level->display, win);
2708                 current_cursor = cursor;
2709         }
2710         else
2711 // don't change cursor if overridden
2712         if((!top_level->is_hourglass && !is_transparent) ||
2713                 override)
2714         {
2715                 XDefineCursor(top_level->display, win, get_cursor_struct(cursor));
2716                 if(flush) this->flush();
2717         }
2718
2719         if(!override) current_cursor = cursor;
2720 }
2721
2722 void BC_WindowBase::set_x_cursor(int cursor)
2723 {
2724         temp_cursor = XCreateFontCursor(top_level->display, cursor);
2725         XDefineCursor(top_level->display, win, temp_cursor);
2726         current_cursor = cursor;
2727         flush();
2728 }
2729
2730 int BC_WindowBase::get_cursor()
2731 {
2732         return current_cursor;
2733 }
2734
2735 void BC_WindowBase::start_hourglass()
2736 {
2737         top_level->start_hourglass_recursive();
2738         top_level->flush();
2739 }
2740
2741 void BC_WindowBase::stop_hourglass()
2742 {
2743         top_level->stop_hourglass_recursive();
2744         top_level->flush();
2745 }
2746
2747 void BC_WindowBase::start_hourglass_recursive()
2748 {
2749         if(this == top_level)
2750         {
2751                 hourglass_total++;
2752                 is_hourglass = 1;
2753         }
2754
2755         if(!is_transparent)
2756         {
2757                 set_cursor(HOURGLASS_CURSOR, 1, 0);
2758                 for(int i = 0; i < subwindows->total; i++)
2759                 {
2760                         subwindows->values[i]->start_hourglass_recursive();
2761                 }
2762         }
2763 }
2764
2765 void BC_WindowBase::stop_hourglass_recursive()
2766 {
2767         if(this == top_level)
2768         {
2769                 if(hourglass_total == 0) return;
2770                 top_level->hourglass_total--;
2771         }
2772
2773         if(!top_level->hourglass_total)
2774         {
2775                 top_level->is_hourglass = 0;
2776
2777 // Cause set_cursor to perform change
2778                 if(!is_transparent)
2779                         set_cursor(current_cursor, 1, 0);
2780
2781                 for(int i = 0; i < subwindows->total; i++)
2782                 {
2783                         subwindows->values[i]->stop_hourglass_recursive();
2784                 }
2785         }
2786 }
2787
2788
2789
2790
2791 XFontStruct* BC_WindowBase::get_font_struct(int font)
2792 {
2793 // Clear out unrelated flags
2794         if(font & BOLDFACE) font ^= BOLDFACE;
2795
2796         switch(font) {
2797                 case SMALLFONT:  return top_level->smallfont;  break;
2798                 case MEDIUMFONT: return top_level->mediumfont; break;
2799                 case LARGEFONT:  return top_level->largefont;  break;
2800                 case BIGFONT:    return top_level->bigfont;    break;
2801                 case CLOCKFONT:  return top_level->clockfont;  break;
2802         }
2803         return 0;
2804 }
2805
2806 XFontSet BC_WindowBase::get_fontset(int font)
2807 {
2808         XFontSet fs = 0;
2809
2810         if(get_resources()->use_fontset)
2811         {
2812                 switch(font & 0xff) {
2813                         case SMALLFONT:  fs = top_level->smallfontset; break;
2814                         case MEDIUMFONT: fs = top_level->mediumfontset; break;
2815                         case LARGEFONT:  fs = top_level->largefontset; break;
2816                         case BIGFONT:    fs = top_level->bigfontset;   break;
2817                         case CLOCKFONT: fs = top_level->clockfontset; break;
2818                 }
2819         }
2820
2821         return fs;
2822 }
2823
2824 #ifdef HAVE_XFT
2825 XftFont* BC_WindowBase::get_xft_struct(int font)
2826 {
2827         switch(font) {
2828                 case SMALLFONT:    return (XftFont*)top_level->smallfont_xft;
2829                 case MEDIUMFONT:   return (XftFont*)top_level->mediumfont_xft;
2830                 case LARGEFONT:    return (XftFont*)top_level->largefont_xft;
2831                 case BIGFONT:      return (XftFont*)top_level->bigfont_xft;
2832                 case CLOCKFONT:    return (XftFont*)top_level->clockfont_xft;
2833                 case MEDIUMFONT_3D: return (XftFont*)top_level->bold_mediumfont_xft;
2834                 case SMALLFONT_3D:  return (XftFont*)top_level->bold_smallfont_xft;
2835                 case LARGEFONT_3D:  return (XftFont*)top_level->bold_largefont_xft;
2836         }
2837
2838         return 0;
2839 }
2840 #endif
2841
2842
2843 int BC_WindowBase::get_current_font()
2844 {
2845         return top_level->current_font;
2846 }
2847
2848 void BC_WindowBase::set_font(int font)
2849 {
2850         top_level->current_font = font;
2851
2852 #ifdef HAVE_XFT
2853         if(get_resources()->use_xft) {}
2854         else
2855 #endif
2856         if(get_resources()->use_fontset) {
2857                 set_fontset(font);
2858         }
2859
2860         if(get_font_struct(font))
2861         {
2862                 XSetFont(top_level->display, top_level->gc, get_font_struct(font)->fid);
2863         }
2864
2865         return;
2866 }
2867
2868 void BC_WindowBase::set_fontset(int font)
2869 {
2870         XFontSet fs = 0;
2871
2872         if(get_resources()->use_fontset) {
2873                 switch(font) {
2874                         case SMALLFONT:  fs = top_level->smallfontset; break;
2875                         case MEDIUMFONT: fs = top_level->mediumfontset; break;
2876                         case LARGEFONT:  fs = top_level->largefontset; break;
2877                         case BIGFONT:    fs = top_level->bigfontset;   break;
2878                         case CLOCKFONT:  fs = top_level->clockfontset; break;
2879                 }
2880         }
2881
2882         curr_fontset = fs;
2883 }
2884
2885
2886 XFontSet BC_WindowBase::get_curr_fontset(void)
2887 {
2888         if(get_resources()->use_fontset)
2889                 return curr_fontset;
2890         return 0;
2891 }
2892
2893 int BC_WindowBase::get_single_text_width(int font, const char *text, int length)
2894 {
2895 #ifdef HAVE_XFT
2896         if(get_resources()->use_xft && get_xft_struct(font))
2897         {
2898                 XGlyphInfo extents;
2899 #ifdef X_HAVE_UTF8_STRING
2900                 if(get_resources()->locale_utf8)
2901                 {
2902                         xftTextExtentsUtf8(top_level->display,
2903                                 get_xft_struct(font),
2904                                 (const XftChar8 *)text,
2905                                 length,
2906                                 &extents);
2907                 }
2908                 else
2909 #endif
2910                 {
2911                         xftTextExtents8(top_level->display,
2912                                 get_xft_struct(font),
2913                                 (const XftChar8 *)text,
2914                                 length,
2915                                 &extents);
2916                 }
2917                 return extents.xOff;
2918         }
2919         else
2920 #endif
2921         if(get_resources()->use_fontset && top_level->get_fontset(font))
2922                 return XmbTextEscapement(top_level->get_fontset(font), text, length);
2923         else
2924         if(get_font_struct(font))
2925                 return XTextWidth(get_font_struct(font), text, length);
2926         else
2927         {
2928                 int w = 0;
2929                 switch(font)
2930                 {
2931                         case MEDIUM_7SEGMENT:
2932                                 return get_resources()->medium_7segment[0]->get_w() * length;
2933                                 break;
2934
2935                         default:
2936                                 return 0;
2937                 }
2938                 return w;
2939         }
2940 }
2941
2942 int BC_WindowBase::get_text_width(int font, const char *text, int length)
2943 {
2944         int i, j, w = 0, line_w = 0;
2945         if(length < 0) length = strlen(text);
2946
2947         for(i = 0, j = 0; i <= length; i++)
2948         {
2949                 line_w = 0;
2950                 if(text[i] == '\n')
2951                 {
2952                         line_w = get_single_text_width(font, &text[j], i - j);
2953                         j = i + 1;
2954                 }
2955                 else
2956                 if(text[i] == 0)
2957                 {
2958                         line_w = get_single_text_width(font, &text[j], length - j);
2959                 }
2960                 if(line_w > w) w = line_w;
2961         }
2962
2963         if(i > length && w == 0)
2964         {
2965                 w = get_single_text_width(font, text, length);
2966         }
2967
2968         return w;
2969 }
2970
2971 int BC_WindowBase::get_text_width(int font, const wchar_t *text, int length)
2972 {
2973         int i, j, w = 0;
2974         if( length < 0 ) length = wcslen(text);
2975
2976         for( i=j=0; i<length && text[i]; ++i ) {
2977                 if( text[i] != '\n' ) continue;
2978                 if( i > j ) {
2979                         int lw = get_single_text_width(font, &text[j], i-j);
2980                         if( w < lw ) w = lw;
2981                 }
2982                 j = i + 1;
2983         }
2984         if( j < length ) {
2985                 int lw = get_single_text_width(font, &text[j], length-j);
2986                 if( w < lw ) w = lw;
2987         }
2988
2989         return w;
2990 }
2991
2992 int BC_WindowBase::get_text_ascent(int font)
2993 {
2994 #ifdef HAVE_XFT
2995         XftFont *fstruct;
2996         if( (fstruct = get_xft_struct(font)) != 0 )
2997                 return fstruct->ascent;
2998 #endif
2999         if(get_resources()->use_fontset && top_level->get_fontset(font))
3000         {
3001                 XFontSetExtents *extents;
3002
3003                 extents = XExtentsOfFontSet(top_level->get_fontset(font));
3004                 return -extents->max_logical_extent.y;
3005         }
3006
3007         if(get_font_struct(font))
3008                 return top_level->get_font_struct(font)->ascent;
3009
3010         switch(font) {
3011                 case MEDIUM_7SEGMENT:
3012                         return get_resources()->medium_7segment[0]->get_h();
3013         }
3014         return 0;
3015 }
3016
3017 int BC_WindowBase::get_text_descent(int font)
3018 {
3019 #ifdef HAVE_XFT
3020         XftFont *fstruct;
3021         if( (fstruct = get_xft_struct(font)) != 0 )
3022                 return fstruct->descent;
3023 #endif
3024         if(get_resources()->use_fontset && top_level->get_fontset(font)) {
3025                 XFontSetExtents *extents;
3026                 extents = XExtentsOfFontSet(top_level->get_fontset(font));
3027                 return (extents->max_logical_extent.height
3028                         + extents->max_logical_extent.y);
3029         }
3030
3031         if(get_font_struct(font))
3032                 return top_level->get_font_struct(font)->descent;
3033
3034         return 0;
3035 }
3036
3037 int BC_WindowBase::get_text_height(int font, const char *text)
3038 {
3039         int rowh;
3040 #ifdef HAVE_XFT
3041         XftFont *fstruct;
3042         if( (fstruct = get_xft_struct(font)) != 0 )
3043                 rowh = fstruct->height;
3044         else
3045 #endif
3046                 rowh = get_text_ascent(font) + get_text_descent(font);
3047
3048         if(!text) return rowh;
3049
3050 // Add height of lines
3051         int h = 0, i, length = strlen(text);
3052         for(i = 0; i <= length; i++)
3053         {
3054                 if(text[i] == '\n')
3055                         h++;
3056                 else
3057                 if(text[i] == 0)
3058                         h++;
3059         }
3060         return h * rowh;
3061 }
3062
3063 // truncate the text with ... & return a new string
3064 char *BC_WindowBase::get_truncated_text(int font, const char *text, int max_w)
3065 {
3066         char *result = cstrdup(text);
3067         int text_w = get_text_width(font, text);
3068         int ci = -1, len = strlen(text);
3069         if( text_w > max_w ) {
3070 // get center of string
3071                 int cx = text_w/2, best = INT_MAX;
3072                 for( int i=1; i<=len; ++i ) {
3073                         int cw = get_text_width(font, text, i);
3074                         if( abs(cw-cx) < abs(best-cx) ) {
3075                                 best = cw;  ci = i;
3076                         }
3077                 }
3078         }
3079         if( ci > 0 && ci < len-1 ) {
3080 // insert ... in the center
3081                 result[ci-1] = result[ci] = result[ci+1] = '.';
3082
3083                 while( ci-2>=0 && ci+2<(int)strlen(result) &&
3084                        get_text_width(font, result) > max_w ) {
3085 // take away a character from the longer side
3086                         int left_w = get_text_width(font, result, ci-2);
3087                         int right_w = get_text_width(font, result + ci+3);
3088                         int i = left_w > right_w ? --ci-1 : ci+2;
3089                         while( (result[i]=result[i+1])!=0 ) ++i;
3090                 }
3091         }
3092
3093         return result;
3094 }
3095
3096 BC_Bitmap* BC_WindowBase::new_bitmap(int w, int h, int color_model)
3097 {
3098         if(color_model < 0) color_model = top_level->get_color_model();
3099         return new BC_Bitmap(top_level, w, h, color_model);
3100 }
3101
3102 void BC_WindowBase::init_wait()
3103 {
3104 #ifndef SINGLE_THREAD
3105         if(window_type != MAIN_WINDOW)
3106                 top_level->init_wait();
3107         init_lock->lock("BC_WindowBase::init_wait");
3108         init_lock->unlock();
3109 #endif
3110 }
3111
3112 int BC_WindowBase::accel_available(int color_model, int lock_it)
3113 {
3114         if( window_type != MAIN_WINDOW )
3115                 return top_level->accel_available(color_model, lock_it);
3116         if( lock_it )
3117                 lock_window("BC_WindowBase::accel_available");
3118
3119         switch(color_model) {
3120         case BC_YUV420P:
3121                 grab_port_id(color_model);
3122                 break;
3123
3124         case BC_YUV422:
3125                 grab_port_id(color_model);
3126                 break;
3127
3128         default:
3129                 break;
3130         }
3131
3132         if( lock_it )
3133                 unlock_window();
3134 //printf("BC_WindowBase::accel_available %d %d\n", color_model, xvideo_port_id);
3135         return xvideo_port_id >= 0 ? 1 : 0;
3136 }
3137
3138
3139 int BC_WindowBase::grab_port_id(int color_model)
3140 {
3141         if( !get_resources()->use_xvideo ||     // disabled
3142             !get_resources()->use_shm )         // Only local server is fast enough.
3143                 return -1;
3144         if( xvideo_port_id >= 0 )
3145                 return xvideo_port_id;
3146
3147         unsigned int ver, rev, reqBase, eventBase, errorBase;
3148         if( Success != XvQueryExtension(display, // XV extension is available
3149                     &ver, &rev, &reqBase, &eventBase, &errorBase) )
3150                 return -1;
3151
3152 // XV adaptors are available
3153         unsigned int numAdapt = 0;
3154         XvAdaptorInfo *info = 0;
3155         XvQueryAdaptors(display, DefaultRootWindow(display), &numAdapt, &info);
3156         if( !numAdapt )
3157                 return -1;
3158
3159 // Translate from color_model to X color model
3160         int x_color_model = BC_CModels::bc_to_x(color_model);
3161
3162 // Get adaptor with desired color model
3163         for( int i = 0; i < (int)numAdapt && xvideo_port_id == -1; i++) {
3164                 if( !(info[i].type & XvImageMask) || !info[i].num_ports ) continue;
3165 // adaptor supports XvImages
3166                 int numFormats = 0, numPorts = info[i].num_ports;
3167                 XvImageFormatValues *formats =
3168                         XvListImageFormats(display, info[i].base_id, &numFormats);
3169                 if( !formats ) continue;
3170
3171                 for( int j=0; j<numFormats && xvideo_port_id<0; ++j ) {
3172                         if( formats[j].id != x_color_model ) continue;
3173 // this adaptor supports the desired format, grab a port
3174                         for( int k=0; k<numPorts; ++k ) {
3175                                 if( Success == XvGrabPort(top_level->display,
3176                                         info[i].base_id+k, CurrentTime) ) {
3177 //printf("BC_WindowBase::grab_port_id %llx\n", info[i].base_id);
3178                                         xvideo_port_id = info[i].base_id + k;
3179                                         break;
3180                                 }
3181                         }
3182                 }
3183                 XFree(formats);
3184         }
3185
3186         XvFreeAdaptorInfo(info);
3187
3188         return xvideo_port_id;
3189 }
3190
3191
3192 int BC_WindowBase::show_window(int flush)
3193 {
3194         for(int i = 0; i < subwindows->size(); i++)
3195         {
3196                 subwindows->get(i)->show_window(0);
3197         }
3198
3199         XMapWindow(top_level->display, win);
3200         if(flush) XFlush(top_level->display);
3201 //      XSync(top_level->display, 0);
3202         hidden = 0;
3203         return 0;
3204 }
3205
3206 int BC_WindowBase::hide_window(int flush)
3207 {
3208         for(int i = 0; i < subwindows->size(); i++)
3209         {
3210                 subwindows->get(i)->hide_window(0);
3211         }
3212
3213         XUnmapWindow(top_level->display, win);
3214         if(flush) this->flush();
3215         hidden = 1;
3216         return 0;
3217 }
3218
3219 BC_MenuBar* BC_WindowBase::add_menubar(BC_MenuBar *menu_bar)
3220 {
3221         subwindows->append((BC_SubWindow*)menu_bar);
3222
3223         menu_bar->parent_window = this;
3224         menu_bar->top_level = this->top_level;
3225         menu_bar->initialize();
3226         return menu_bar;
3227 }
3228
3229 BC_WindowBase* BC_WindowBase::add_popup(BC_WindowBase *window)
3230 {
3231 //printf("BC_WindowBase::add_popup window=%p win=%p\n", window, window->win);
3232         if(this != top_level) return top_level->add_popup(window);
3233         popups.append(window);
3234         return window;
3235 }
3236
3237 void BC_WindowBase::remove_popup(BC_WindowBase *window)
3238 {
3239 //printf("BC_WindowBase::remove_popup %d size=%d window=%p win=%p\n", __LINE__, popups.size(), window, window->win);
3240         if(this != top_level)
3241                 top_level->remove_popup(window);
3242         else
3243                 popups.remove(window);
3244 //printf("BC_WindowBase::remove_popup %d size=%d window=%p win=%p\n", __LINE__, popups.size(), window, window->win);
3245 }
3246
3247
3248 BC_WindowBase* BC_WindowBase::add_subwindow(BC_WindowBase *subwindow)
3249 {
3250         subwindows->append(subwindow);
3251
3252         if(subwindow->bg_color == -1) subwindow->bg_color = this->bg_color;
3253
3254 // parent window must be set before the subwindow initialization
3255         subwindow->parent_window = this;
3256         subwindow->top_level = this->top_level;
3257
3258 // Execute derived initialization
3259         subwindow->initialize();
3260         return subwindow;
3261 }
3262
3263
3264 BC_WindowBase* BC_WindowBase::add_tool(BC_WindowBase *subwindow)
3265 {
3266         return add_subwindow(subwindow);
3267 }
3268
3269 int BC_WindowBase::flash(int x, int y, int w, int h, int flush)
3270 {
3271         if( !top_level->flash_enabled ) return 0;
3272 //printf("BC_WindowBase::flash %d %d %d %d %d\n", __LINE__, w, h, this->w, this->h);
3273         set_opaque();
3274         XSetWindowBackgroundPixmap(top_level->display, win, pixmap->opaque_pixmap);
3275         if(x >= 0)
3276         {
3277                 XClearArea(top_level->display, win, x, y, w, h, 0);
3278         }
3279         else
3280         {
3281                 XClearWindow(top_level->display, win);
3282         }
3283
3284         if(flush)
3285                 this->flush();
3286         return 0;
3287 }
3288
3289 int BC_WindowBase::flash(int flush)
3290 {
3291         flash(-1, -1, -1, -1, flush);
3292         return 0;
3293 }
3294
3295 void BC_WindowBase::flush()
3296 {
3297         //if(!get_window_lock())
3298         //      printf("BC_WindowBase::flush %s not locked\n", top_level->title);
3299         // X gets hosed if Flush/Sync are not user locked (at libX11-1.1.5 / libxcb-1.1.91)
3300         //   _XReply deadlocks in condition_wait waiting for xlib lock when waiters!=-1
3301         int locked  = get_window_lock();
3302         if( !locked ) lock_window("BC_WindowBase::flush");
3303         XFlush(top_level->display);
3304         if( !locked ) unlock_window();
3305 }
3306
3307 void BC_WindowBase::sync_display()
3308 {
3309         int locked  = get_window_lock();
3310         if( !locked ) lock_window("BC_WindowBase::sync_display");
3311         XSync(top_level->display, False);
3312         if( !locked ) unlock_window();
3313 }
3314
3315 int BC_WindowBase::get_window_lock()
3316 {
3317 #ifdef SINGLE_THREAD
3318         return BC_Display::display_global->get_display_locked();
3319 #else
3320         return top_level->window_lock;
3321 #endif
3322 }
3323
3324 int BC_WindowBase::lock_window(const char *location)
3325 {
3326         if(top_level && top_level != this)
3327         {
3328                 top_level->lock_window(location);
3329         }
3330         else
3331         if(top_level)
3332         {
3333                 SET_LOCK(this, title, location);
3334 #ifdef SINGLE_THREAD
3335                 BC_Display::lock_display(location);
3336 #else
3337                 XLockDisplay(top_level->display);
3338                 top_level->display_lock_owner = pthread_self();
3339 #endif
3340                 SET_LOCK2
3341                 ++top_level->window_lock;
3342         }
3343         else
3344         {
3345                 printf("BC_WindowBase::lock_window top_level NULL\n");
3346         }
3347         return 0;
3348 }
3349
3350 int BC_WindowBase::unlock_window()
3351 {
3352         if(top_level && top_level != this)
3353         {
3354                 top_level->unlock_window();
3355         }
3356         else
3357         if(top_level)
3358         {
3359                 UNSET_LOCK(this);
3360                 if( !top_level->window_lock ) {
3361                         printf("BC_WindowBase::unlock_window %s not locked\n", title);
3362                         booby();
3363                 }
3364                 if( top_level->window_lock > 0 )
3365                         if( --top_level->window_lock == 0 )
3366                                 top_level->display_lock_owner = 0;
3367 #ifdef SINGLE_THREAD
3368                 BC_Display::unlock_display();
3369 #else
3370                 XUnlockDisplay(top_level->display);
3371 #endif
3372         }
3373         else
3374         {
3375                 printf("BC_WindowBase::unlock_window top_level NULL\n");
3376         }
3377         return 0;
3378 }
3379
3380 int BC_WindowBase::break_lock()
3381 {
3382         if( !top_level ) return 0;
3383         if( top_level != this ) return top_level->break_lock();
3384         if( top_level->display_lock_owner != pthread_self() ) return 0;
3385         if( top_level->window_lock != 1 ) return 0;
3386         UNSET_LOCK(this);
3387         window_lock = 0;
3388         display_lock_owner = 0;
3389 #ifdef SINGLE_THREAD
3390         BC_Display::unlock_display();
3391 #else
3392         XUnlockDisplay(display);
3393 #endif
3394         return 1;
3395 }
3396
3397 void BC_WindowBase::set_done(int return_value)
3398 {
3399         if(done_set) return;
3400         done_set = 1;
3401         if(window_type != MAIN_WINDOW)
3402                 top_level->set_done(return_value);
3403         else
3404         {
3405 #ifdef SINGLE_THREAD
3406                 this->return_value = return_value;
3407                 BC_Display::display_global->arm_completion(this);
3408                 completion_lock->unlock();
3409 #else // SINGLE_THREAD
3410                 init_wait();
3411                 if( !event_thread ) return;
3412                 XEvent *event = new_xevent();
3413                 XClientMessageEvent *ptr = (XClientMessageEvent*)event;
3414                 event->type = ClientMessage;
3415                 ptr->message_type = SetDoneXAtom;
3416                 ptr->format = 32;
3417                 this->return_value = return_value;
3418 // May lock up here because XSendEvent doesn't work too well
3419 // asynchronous with XNextEvent.
3420 // This causes BC_WindowEvents to forward a copy of the event to run_window where
3421 // it is deleted.
3422 // Deletion of event_thread is done at the end of BC_WindowBase::run_window() - by calling the destructor
3423                 put_event(event);
3424 #endif
3425         }
3426 }
3427
3428 void BC_WindowBase::close(int return_value)
3429 {
3430         hide_window();  flush();
3431         set_done(return_value);
3432 }
3433
3434 int BC_WindowBase::grab(BC_WindowBase *window)
3435 {
3436         if( window->active_grab && this != window->active_grab ) return 0;
3437         window->active_grab = this;
3438         this->grab_active = window;
3439         return 1;
3440 }
3441 int BC_WindowBase::ungrab(BC_WindowBase *window)
3442 {
3443         if( window->active_grab && this != window->active_grab ) return 0;
3444         window->active_grab = 0;
3445         this->grab_active = 0;
3446         return 1;
3447 }
3448 int BC_WindowBase::grab_event_count()
3449 {
3450         int result = 0;
3451 #ifndef SINGLE_THREAD
3452         result = grab_active->get_event_count();
3453 #endif
3454         return result;
3455 }
3456 int BC_WindowBase::grab_buttons()
3457 {
3458         XSync(top_level->display, False);
3459         if( XGrabButton(top_level->display, AnyButton, AnyModifier,
3460                         top_level->rootwin, True, ButtonPressMask | ButtonReleaseMask,
3461                         GrabModeAsync, GrabModeSync, None, None) == GrabSuccess ) {
3462                 set_active_subwindow(this);
3463                 return 0;
3464         }
3465         return 1;
3466 }
3467 void BC_WindowBase::ungrab_buttons()
3468 {
3469         XUngrabButton(top_level->display, AnyButton, AnyModifier, top_level->rootwin);
3470         set_active_subwindow(0);
3471         unhide_cursor();
3472 }
3473 void BC_WindowBase::grab_cursor()
3474 {
3475         Cursor cursor_grab = get_cursor_struct(GRABBED_CURSOR);
3476         XGrabPointer(top_level->display, top_level->rootwin, True,
3477                 PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
3478                 GrabModeAsync, GrabModeAsync, None, cursor_grab, CurrentTime);
3479 }
3480 void BC_WindowBase::ungrab_cursor()
3481 {
3482         XUngrabPointer(top_level->display, CurrentTime);
3483 }
3484
3485 // for get_root_w/h
3486 //   WidthOfScreen/HeightOfScreen of XDefaultScreenOfDisplay
3487 //   this is the bounding box of all the screens
3488
3489 int BC_WindowBase::get_root_w(int lock_display)
3490 {
3491         if(lock_display) lock_window("BC_WindowBase::get_root_w");
3492         Screen *def_screen = XDefaultScreenOfDisplay(top_level->display);
3493         int result = WidthOfScreen(def_screen);
3494         if(lock_display) unlock_window();
3495         return result;
3496 }
3497
3498 int BC_WindowBase::get_root_h(int lock_display)
3499 {
3500         if(lock_display) lock_window("BC_WindowBase::get_root_h");
3501         Screen *def_screen = XDefaultScreenOfDisplay(top_level->display);
3502         int result = HeightOfScreen(def_screen);
3503         if(lock_display) unlock_window();
3504         return result;
3505 }
3506
3507 XineramaScreenInfo *
3508 BC_WindowBase::get_xinerama_info(int screen)
3509 {
3510         if( !xinerama_info || !xinerama_screens ) return 0;
3511         if( screen >= 0 ) {
3512                 for( int i=0; i<xinerama_screens; ++i )
3513                         if( xinerama_info[i].screen_number == screen )
3514                                 return &xinerama_info[i];
3515                 return 0;
3516         }
3517         int top_x = get_x(), top_y = get_y();
3518         if(  BC_DisplayInfo::left_border >= 0 ) top_x +=  BC_DisplayInfo::left_border;
3519         if(  BC_DisplayInfo::top_border >= 0 ) top_y +=  BC_DisplayInfo::top_border;
3520         for( int i=0; i<xinerama_screens; ++i ) {
3521                 int scr_y = top_y - xinerama_info[i].y_org;
3522                 if( scr_y < 0 || scr_y >= xinerama_info[i].height ) continue;
3523                 int scr_x = top_x - xinerama_info[i].x_org;
3524                 if( scr_x >= 0 && scr_x < xinerama_info[i].width )
3525                         return &xinerama_info[i];
3526         }
3527         return 0;
3528 }
3529
3530 void BC_WindowBase::get_fullscreen_geometry(int &wx, int &wy, int &ww, int &wh)
3531 {
3532         XineramaScreenInfo *info = top_level->get_xinerama_info(-1);
3533         if( info ) {
3534                 wx = info->x_org;  wy = info->y_org;
3535                 ww = info->width;  wh = info->height;
3536         }
3537         else {
3538                 wx = get_screen_x(0, -1);
3539                 wy = get_screen_y(0, -1);
3540                 int scr_w0 = get_screen_w(0, 0);
3541                 int root_w = get_root_w(0);
3542                 int root_h = get_root_h(0);
3543                 if( root_w > scr_w0 ) { // multi-headed
3544                         if( wx >= scr_w0 ) {
3545                                 // assumes right side is the big one
3546                                 ww = root_w - scr_w0;
3547                                 wh = root_h;
3548                         }
3549                         else {
3550                                 // use same aspect ratio to compute left height
3551                                 ww = scr_w0;
3552                                 wh = (w*root_h) / (root_w-scr_w0);
3553                         }
3554                 }
3555                 else {
3556                         ww = root_w;
3557                         wh = root_h;
3558                 }
3559         }
3560 }
3561
3562 int BC_WindowBase::get_screen_x(int lock_display, int screen)
3563 {
3564         int result = -1;
3565         if(lock_display) lock_window("BC_WindowBase::get_screen_x");
3566         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3567         if( !info ) {
3568                 result = 0;
3569                 int root_w = get_root_w(0);
3570                 int root_h = get_root_h(0);
3571 // Shift X based on position of current window if dual head
3572                 if( (float)root_w/root_h > 1.8 ) {
3573                         root_w = get_screen_w(0, 0);
3574                         if( top_level->get_x() >= root_w )
3575                                 result = root_w;
3576                 }
3577         }
3578         else
3579                 result = info->x_org;
3580         if(lock_display) unlock_window();
3581         return result;
3582 }
3583
3584 int BC_WindowBase::get_screen_y(int lock_display, int screen)
3585 {
3586         if(lock_display) lock_window("BC_WindowBase::get_screen_y");
3587         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3588         int result = !info ? 0 : info->y_org;
3589         if(lock_display) unlock_window();
3590         return result;
3591 }
3592
3593 int BC_WindowBase::get_screen_w(int lock_display, int screen)
3594 {
3595         int result = -1;
3596         if(lock_display) lock_window("BC_WindowBase::get_screen_w");
3597         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3598         if( !info ) {
3599                 int width = get_root_w(0);
3600                 int height = get_root_h(0);
3601                 if( (float)width/height > 1.8 ) {
3602                         // If dual head, the screen width is > 16x9
3603                         // but we only want to fill one screen
3604                         // this code assumes the "big" screen is on the right
3605                         int scr_w0 = width / 2;
3606                         switch( height ) {
3607                         case 600:  scr_w0 = 800;   break;
3608                         case 720:  scr_w0 = 1280;  break;
3609                         case 1024: scr_w0 = 1280;  break;
3610                         case 1200: scr_w0 = 1600;  break;
3611                         case 1080: scr_w0 = 1920;  break;
3612                         }
3613                         int scr_w1 = width - scr_w0;
3614                         result = screen > 0 ? scr_w1 :
3615                                 screen == 0 ? scr_w0 :
3616                                 top_level->get_x() < scr_w0 ? scr_w0 : scr_w1;
3617                 }
3618                 else
3619                         result = width;
3620         }
3621         else
3622                 result = info->width;
3623         if(lock_display) unlock_window();
3624         return result;
3625 }
3626
3627 int BC_WindowBase::get_screen_h(int lock_display, int screen)
3628 {
3629         if(lock_display) lock_window("BC_WindowBase::get_screen_h");
3630         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3631         int result = info ? info->height : get_root_h(0);
3632         if(lock_display) unlock_window();
3633         return result;
3634 }
3635
3636 // Bottom right corner
3637 int BC_WindowBase::get_x2()
3638 {
3639         return w + x;
3640 }
3641
3642 int BC_WindowBase::get_y2()
3643 {
3644         return y + h;
3645 }
3646
3647 int BC_WindowBase::get_video_on()
3648 {
3649         return video_on;
3650 }
3651
3652 int BC_WindowBase::get_hidden()
3653 {
3654         return top_level->hidden;
3655 }
3656
3657 int BC_WindowBase::cursor_inside()
3658 {
3659         return (top_level->cursor_x >= 0 &&
3660                         top_level->cursor_y >= 0 &&
3661                         top_level->cursor_x < w &&
3662                         top_level->cursor_y < h);
3663 }
3664
3665 BC_WindowBase* BC_WindowBase::get_top_level()
3666 {
3667         return top_level;
3668 }
3669
3670 BC_WindowBase* BC_WindowBase::get_parent()
3671 {
3672         return parent_window;
3673 }
3674
3675 int BC_WindowBase::get_color_model()
3676 {
3677         return top_level->color_model;
3678 }
3679
3680 BC_Resources* BC_WindowBase::get_resources()
3681 {
3682         return BC_WindowBase::resources;
3683 }
3684
3685 BC_Synchronous* BC_WindowBase::get_synchronous()
3686 {
3687         return BC_WindowBase::resources->get_synchronous();
3688 }
3689
3690 int BC_WindowBase::get_bg_color()
3691 {
3692         return bg_color;
3693 }
3694
3695 void BC_WindowBase::set_bg_color(int color)
3696 {
3697         this->bg_color = color;
3698 }
3699
3700 BC_Pixmap* BC_WindowBase::get_bg_pixmap()
3701 {
3702         return bg_pixmap;
3703 }
3704
3705 void BC_WindowBase::set_active_subwindow(BC_WindowBase *subwindow)
3706 {
3707         top_level->active_subwindow = subwindow;
3708 }
3709
3710 int BC_WindowBase::activate()
3711 {
3712         return 0;
3713 }
3714
3715 int BC_WindowBase::deactivate()
3716 {
3717         if(window_type == MAIN_WINDOW)
3718         {
3719                 if( top_level->active_menubar ) {
3720                         top_level->active_menubar->deactivate();
3721                         top_level->active_menubar = 0;
3722                 }
3723                 if( top_level->active_popup_menu ) {
3724                         top_level->active_popup_menu->deactivate();
3725                         top_level->active_popup_menu = 0;
3726                 }
3727                 if( top_level->active_subwindow ) {
3728                         top_level->active_subwindow->deactivate();
3729                         top_level->active_subwindow = 0;
3730                 }
3731                 if( top_level->motion_events && top_level->last_motion_win == this->win )
3732                         top_level->motion_events = 0;
3733
3734         }
3735         return 0;
3736 }
3737
3738 int BC_WindowBase::cycle_textboxes(int amount)
3739 {
3740         int result = 0;
3741         BC_WindowBase *new_textbox = 0;
3742
3743         if(amount > 0)
3744         {
3745                 BC_WindowBase *first_textbox = 0;
3746                 find_next_textbox(&first_textbox, &new_textbox, result);
3747                 if(!new_textbox) new_textbox = first_textbox;
3748
3749         }
3750         else
3751         if(amount < 0)
3752         {
3753                 BC_WindowBase *last_textbox = 0;
3754                 find_prev_textbox(&last_textbox, &new_textbox, result);
3755                 if(!new_textbox) new_textbox = last_textbox;
3756
3757         }
3758
3759         if(new_textbox != active_subwindow)
3760         {
3761                 deactivate();
3762                 new_textbox->activate();
3763         }
3764
3765         return 0;
3766 }
3767
3768 int BC_WindowBase::find_next_textbox(BC_WindowBase **first_textbox, BC_WindowBase **next_textbox, int &result)
3769 {
3770 // Search subwindows for textbox
3771         for(int i = 0; i < subwindows->total && result < 2; i++)
3772         {
3773                 BC_WindowBase *test_subwindow = subwindows->values[i];
3774                 test_subwindow->find_next_textbox(first_textbox, next_textbox, result);
3775         }
3776
3777         if(result < 2)
3778         {
3779                 if(uses_text())
3780                 {
3781                         if(!*first_textbox) *first_textbox = this;
3782
3783                         if(result < 1)
3784                         {
3785                                 if(top_level->active_subwindow == this)
3786                                         result++;
3787                         }
3788                         else
3789                         {
3790                                 result++;
3791                                 *next_textbox = this;
3792                         }
3793                 }
3794         }
3795         return 0;
3796 }
3797
3798 int BC_WindowBase::find_prev_textbox(BC_WindowBase **last_textbox, BC_WindowBase **prev_textbox, int &result)
3799 {
3800         if(result < 2)
3801         {
3802                 if(uses_text())
3803                 {
3804                         if(!*last_textbox) *last_textbox = this;
3805
3806                         if(result < 1)
3807                         {
3808                                 if(top_level->active_subwindow == this)
3809                                         result++;
3810                         }
3811                         else
3812                         {
3813                                 result++;
3814                                 *prev_textbox = this;
3815                         }
3816                 }
3817         }
3818
3819 // Search subwindows for textbox
3820         for(int i = subwindows->total - 1; i >= 0 && result < 2; i--)
3821         {
3822                 BC_WindowBase *test_subwindow = subwindows->values[i];
3823                 test_subwindow->find_prev_textbox(last_textbox, prev_textbox, result);
3824         }
3825         return 0;
3826 }
3827
3828 BC_Clipboard* BC_WindowBase::get_clipboard()
3829 {
3830 #ifdef SINGLE_THREAD
3831         return BC_Display::display_global->clipboard;
3832 #else
3833         return top_level->clipboard;
3834 #endif
3835 }
3836
3837 Atom BC_WindowBase::to_clipboard(const char *data, long len, int clipboard_num)
3838 {
3839         return get_clipboard()->to_clipboard(this, data, len, clipboard_num);
3840 }
3841
3842 long BC_WindowBase::from_clipboard(char *data, long maxlen, int clipboard_num)
3843 {
3844         return get_clipboard()->from_clipboard(data, maxlen, clipboard_num);
3845 }
3846
3847 long BC_WindowBase::clipboard_len(int clipboard_num)
3848 {
3849         return get_clipboard()->clipboard_len(clipboard_num);
3850 }
3851
3852 int BC_WindowBase::do_selection_clear(Window win)
3853 {
3854         top_level->event_win = win;
3855         return dispatch_selection_clear();
3856 }
3857
3858 int BC_WindowBase::dispatch_selection_clear()
3859 {
3860         int result = 0;
3861         for( int i=0; i<subwindows->total && !result; ++i )
3862                 result = subwindows->values[i]->dispatch_selection_clear();
3863         if( !result )
3864                 result = selection_clear_event();
3865         return result;
3866 }
3867
3868
3869 void BC_WindowBase::get_relative_cursor(int &x, int &y, int lock_window)
3870 {
3871         int abs_x, abs_y, win_x, win_y;
3872         unsigned int temp_mask;
3873         Window temp_win;
3874
3875         if(lock_window) this->lock_window("BC_WindowBase::get_relative_cursor");
3876         XQueryPointer(top_level->display, top_level->win,
3877            &temp_win, &temp_win, &abs_x, &abs_y, &win_x, &win_y,
3878            &temp_mask);
3879
3880         XTranslateCoordinates(top_level->display, top_level->rootwin,
3881            win, abs_x, abs_y, &x, &y, &temp_win);
3882         if(lock_window) this->unlock_window();
3883 }
3884 int BC_WindowBase::get_relative_cursor_x(int lock_window)
3885 {
3886         int x, y;
3887         get_relative_cursor(x, y, lock_window);
3888         return x;
3889 }
3890 int BC_WindowBase::get_relative_cursor_y(int lock_window)
3891 {
3892         int x, y;
3893         get_relative_cursor(x, y, lock_window);
3894         return y;
3895 }
3896
3897 void BC_WindowBase::get_abs_cursor(int &abs_x, int &abs_y, int lock_window)
3898 {
3899         int win_x, win_y;
3900         unsigned int temp_mask;
3901         Window temp_win;
3902
3903         if(lock_window) this->lock_window("BC_WindowBase::get_abs_cursor");
3904         XQueryPointer(top_level->display, top_level->win,
3905                 &temp_win, &temp_win, &abs_x, &abs_y, &win_x, &win_y,
3906                 &temp_mask);
3907         if(lock_window) this->unlock_window();
3908 }
3909 int BC_WindowBase::get_abs_cursor_x(int lock_window)
3910 {
3911         int abs_x, abs_y;
3912         get_abs_cursor(abs_x, abs_y, lock_window);
3913         return abs_x;
3914 }
3915 int BC_WindowBase::get_abs_cursor_y(int lock_window)
3916 {
3917         int abs_x, abs_y;
3918         get_abs_cursor(abs_x, abs_y, lock_window);
3919         return abs_y;
3920 }
3921
3922 void BC_WindowBase::get_pop_cursor(int &px, int &py, int lock_window)
3923 {
3924         int xmargin = xS(100), ymargin = yS(100);
3925         get_abs_cursor(px, py, lock_window);
3926         if( px < xmargin ) px = xmargin;
3927         if( py < ymargin ) py = ymargin;
3928         int wd = get_screen_w(lock_window,-1) - xmargin;
3929         if( px > wd ) px = wd;
3930         int ht = get_screen_h(lock_window,-1) - ymargin;
3931         if( py > ht ) py = ht;
3932 }
3933 int BC_WindowBase::get_pop_cursor_x(int lock_window)
3934 {
3935         int px, py;
3936         get_pop_cursor(px, py, lock_window);
3937         return px;
3938 }
3939 int BC_WindowBase::get_pop_cursor_y(int lock_window)
3940 {
3941         int px, py;
3942         get_pop_cursor(px, py, lock_window);
3943         return py;
3944 }
3945
3946 int BC_WindowBase::match_window(Window win)
3947 {
3948         if (this->win == win) return 1;
3949         int result = 0;
3950         for(int i = 0; i < subwindows->total; i++) {
3951                 result = subwindows->values[i]->match_window(win);
3952                 if (result) return result;
3953         }
3954         return 0;
3955
3956 }
3957
3958 int BC_WindowBase::get_cursor_over_window()
3959 {
3960         int abs_x, abs_y, win_x, win_y;
3961         unsigned int mask_return;
3962         Window root_return, child_return;
3963
3964         int ret = XQueryPointer(top_level->display, top_level->rootwin,
3965                 &root_return, &child_return, &abs_x, &abs_y,
3966                 &win_x, &win_y, &mask_return);
3967         if( ret && child_return == None ) ret = 0;
3968         if( ret && win != child_return )
3969                 ret = top_level->match_window(child_return);
3970 // query pointer can return a window manager window with this top_level as a child
3971 //  for kde this can be two levels deep
3972         unsigned int nchildren_return = 0;
3973         Window parent_return, *children_return = 0;
3974         Window top_win = top_level->win;
3975         while( !ret && top_win != top_level->rootwin && top_win != root_return &&
3976                 XQueryTree(top_level->display, top_win, &root_return,
3977                         &parent_return, &children_return, &nchildren_return) ) {
3978                 if( children_return ) XFree(children_return);
3979                 if( (top_win=parent_return) == child_return ) ret = 1;
3980         }
3981         return ret;
3982 }
3983
3984 int BC_WindowBase::cursor_above()
3985 {
3986         int rx, ry;
3987         get_relative_cursor(rx, ry);
3988         return rx < 0 || rx >= get_w() ||
3989                 ry < 0 || ry >= get_h() ? 0 : 1;
3990 }
3991
3992 int BC_WindowBase::get_drag_x()
3993 {
3994         return top_level->drag_x;
3995 }
3996
3997 int BC_WindowBase::get_drag_y()
3998 {
3999         return top_level->drag_y;
4000 }
4001
4002 int BC_WindowBase::get_cursor_x()
4003 {
4004         return top_level->cursor_x;
4005 }
4006
4007 int BC_WindowBase::get_cursor_y()
4008 {
4009         return top_level->cursor_y;
4010 }
4011
4012 int BC_WindowBase::dump_windows()
4013 {
4014         printf("\tBC_WindowBase::dump_windows window=%p win=%p '%s', %dx%d+%d+%d %s\n",
4015                 this, (void*)this->win, title, w,h,x,y, typeid(*this).name());
4016         for(int i = 0; i < subwindows->size(); i++)
4017                 subwindows->get(i)->dump_windows();
4018         for(int i = 0; i < popups.size(); i++) {
4019                 BC_WindowBase *p = popups[i];
4020                 printf("\tBC_WindowBase::dump_windows popup=%p win=%p '%s', %dx%d+%d+%d %s\n",
4021                         p, (void*)p->win, p->title, p->w,p->h,p->x,p->y, typeid(*p).name());
4022         }
4023         return 0;
4024 }
4025
4026 int BC_WindowBase::is_event_win()
4027 {
4028         return this->win == top_level->event_win;
4029 }
4030
4031 void BC_WindowBase::set_dragging(int value)
4032 {
4033         is_dragging = value;
4034 }
4035
4036 int BC_WindowBase::get_dragging()
4037 {
4038         return is_dragging;
4039 }
4040
4041 int BC_WindowBase::get_buttonpress()
4042 {
4043         return top_level->button_number;
4044 }
4045
4046 int BC_WindowBase::get_button_down()
4047 {
4048         return top_level->button_down;
4049 }
4050
4051 int BC_WindowBase::alt_down()
4052 {
4053         return top_level->alt_mask;
4054 }
4055
4056 int BC_WindowBase::shift_down()
4057 {
4058         return top_level->shift_mask;
4059 }
4060
4061 int BC_WindowBase::ctrl_down()
4062 {
4063         return top_level->ctrl_mask;
4064 }
4065
4066 wchar_t* BC_WindowBase::get_wkeystring(int *length)
4067 {
4068         if(length)
4069                 *length = top_level->wkey_string_length;
4070         return top_level->wkey_string;
4071 }
4072
4073 #ifdef X_HAVE_UTF8_STRING
4074 char* BC_WindowBase::get_keypress_utf8()
4075 {
4076         return top_level->key_pressed_utf8;
4077 }
4078 #endif
4079
4080
4081 int BC_WindowBase::get_keypress()
4082 {
4083         return top_level->key_pressed;
4084 }
4085
4086 int BC_WindowBase::get_double_click()
4087 {
4088         return top_level->double_click;
4089 }
4090
4091 int BC_WindowBase::get_triple_click()
4092 {
4093         return top_level->triple_click;
4094 }
4095
4096 int BC_WindowBase::get_bgcolor()
4097 {
4098         return bg_color;
4099 }
4100
4101 int BC_WindowBase::resize_window(int w, int h)
4102 {
4103         if(this->w == w && this->h == h) return 0;
4104
4105         if(window_type == MAIN_WINDOW && !allow_resize)
4106         {
4107                 XSizeHints size_hints;
4108                 size_hints.flags = PSize | PMinSize | PMaxSize;
4109                 size_hints.width = w;
4110                 size_hints.height = h;
4111                 size_hints.min_width = w;
4112                 size_hints.max_width = w;
4113                 size_hints.min_height = h;
4114                 size_hints.max_height = h;
4115                 if( this->x > -BC_INFINITY && this->x < BC_INFINITY ) {
4116                         size_hints.flags |= PPosition;
4117                         size_hints.x = this->x;
4118                         size_hints.y = this->y;
4119                 }
4120                 XSetNormalHints(top_level->display, win, &size_hints);
4121         }
4122         XResizeWindow(top_level->display, win, w, h);
4123
4124         this->w = w;
4125         this->h = h;
4126         delete pixmap;
4127         pixmap = new BC_Pixmap(this, w, h);
4128
4129 // Propagate to menubar
4130         for(int i = 0; i < subwindows->total; i++)
4131         {
4132                 subwindows->values[i]->dispatch_resize_event(w, h);
4133         }
4134
4135         draw_background(0, 0, w, h);
4136         if(top_level == this && get_resources()->recursive_resizing)
4137                 resize_history.append(new BC_ResizeCall(w, h));
4138         return 0;
4139 }
4140
4141 // The only way for resize events to be propagated is by updating the internal w and h
4142 int BC_WindowBase::resize_event(int w, int h)
4143 {
4144         if(window_type == MAIN_WINDOW)
4145         {
4146                 this->w = w;
4147                 this->h = h;
4148         }
4149         return 0;
4150 }
4151
4152 int BC_WindowBase::reposition_window(int x, int y)
4153 {
4154         reposition_window(x, y, -1, -1);
4155         return 0;
4156 }
4157
4158
4159 int BC_WindowBase::reposition_window(int x, int y, int w, int h)
4160 {
4161         int resize = 0;
4162
4163 // Some tools set their own dimensions before calling this, causing the
4164 // resize check to skip.
4165         this->x = x;
4166         this->y = y;
4167
4168         if(w > 0 && w != this->w)
4169         {
4170                 resize = 1;
4171                 this->w = w;
4172         }
4173
4174         if(h > 0 && h != this->h)
4175         {
4176                 resize = 1;
4177                 this->h = h;
4178         }
4179
4180 //printf("BC_WindowBase::reposition_window %d %d %d\n", translation_count, x_correction, y_correction);
4181
4182         if(this->w <= 0)
4183                 printf("BC_WindowBase::reposition_window this->w == %d\n", this->w);
4184         if(this->h <= 0)
4185                 printf("BC_WindowBase::reposition_window this->h == %d\n", this->h);
4186
4187         if(translation_count && window_type == MAIN_WINDOW)
4188         {
4189 // KDE shifts window right and down.
4190 // FVWM leaves window alone and adds border around it.
4191                 XMoveResizeWindow(top_level->display, win,
4192                         x - BC_DisplayInfo::auto_reposition_x,
4193                         y - BC_DisplayInfo::auto_reposition_y,
4194                         this->w, this->h);
4195         }
4196         else
4197         {
4198                 XMoveResizeWindow(top_level->display, win, x, y,
4199                         this->w, this->h);
4200         }
4201
4202         if(resize)
4203         {
4204                 delete pixmap;
4205                 pixmap = new BC_Pixmap(this, this->w, this->h);
4206                 clear_box(0,0, this->w, this->h);
4207 // Propagate to menubar
4208                 for(int i = 0; i < subwindows->total; i++)
4209                 {
4210                         subwindows->values[i]->dispatch_resize_event(this->w, this->h);
4211                 }
4212
4213 //              draw_background(0, 0, w, h);
4214         }
4215
4216         return 0;
4217 }
4218
4219 int BC_WindowBase::reposition_window_relative(int dx, int dy, int w, int h)
4220 {
4221         return reposition_window(get_x()+dx, get_y()+dy, w, h);
4222 }
4223
4224 int BC_WindowBase::reposition_window_relative(int dx, int dy)
4225 {
4226         return reposition_window_relative(dx, dy, -1, -1);
4227 }
4228
4229 void BC_WindowBase::set_tooltips(int v)
4230 {
4231         get_resources()->tooltips_enabled = v;
4232 }
4233
4234 void BC_WindowBase::set_force_tooltip(int v)
4235 {
4236         force_tooltip = v;
4237 }
4238
4239 int BC_WindowBase::raise_window(int do_flush)
4240 {
4241         XRaiseWindow(top_level->display, win);
4242         if(do_flush) XFlush(top_level->display);
4243         return 0;
4244 }
4245
4246 int BC_WindowBase::lower_window(int do_flush)
4247 {
4248         XLowerWindow(top_level->display, win);
4249         if(do_flush) XFlush(top_level->display);
4250         return 0;
4251 }
4252
4253 void BC_WindowBase::set_background(VFrame *bitmap)
4254 {
4255         if(bg_pixmap && !shared_bg_pixmap) delete bg_pixmap;
4256
4257         bg_pixmap = new BC_Pixmap(this, bitmap, PIXMAP_OPAQUE);
4258         shared_bg_pixmap = 0;
4259         draw_background(0, 0, w, h);
4260 }
4261
4262 void BC_WindowBase::put_title(const char *text)
4263 {
4264         char *cp = this->title, *ep = cp+sizeof(this->title)-1;
4265         for( const unsigned char *bp = (const unsigned char *)text; *bp && cp<ep; ++bp )
4266                 *cp++ = *bp >= ' ' ? *bp : ' ';
4267         *cp = 0;
4268 }
4269
4270 void BC_WindowBase::set_title(const char *text, int utf8)
4271 {
4272 // utf8>0: wm + net_wm, utf8=0: wm only,  utf<0: net_wm only
4273         put_title(text);
4274         const unsigned char *wm_title = (const unsigned char *)title;
4275         int title_len = strlen((const char *)title);
4276         if( utf8 >= 0 ) {
4277                 Atom xa_wm_name = XA_WM_NAME;
4278                 Atom xa_icon_name = XA_WM_ICON_NAME;
4279                 Atom xa_string = XA_STRING;
4280                 XChangeProperty(display, win, xa_wm_name, xa_string, 8,
4281                                 PropModeReplace, wm_title, title_len);
4282                 XChangeProperty(display, win, xa_icon_name, xa_string, 8,
4283                                 PropModeReplace, wm_title, title_len);
4284         }
4285         if( utf8 != 0 ) {
4286                 Atom xa_net_wm_name = XInternAtom(display, "_NET_WM_NAME", True);
4287                 Atom xa_net_icon_name = XInternAtom(display, "_NET_WM_ICON_NAME", True);
4288                 Atom xa_utf8_string = XInternAtom(display, "UTF8_STRING", True);
4289                 XChangeProperty(display, win, xa_net_wm_name, xa_utf8_string, 8,
4290                                         PropModeReplace, wm_title, title_len);
4291                 XChangeProperty(display, win, xa_net_icon_name, xa_utf8_string, 8,
4292                                         PropModeReplace, wm_title, title_len);
4293         }
4294         flush();
4295 }
4296
4297 const char *BC_WindowBase::get_title()
4298 {
4299         return title;
4300 }
4301
4302 int BC_WindowBase::get_toggle_value()
4303 {
4304         return toggle_value;
4305 }
4306
4307 int BC_WindowBase::get_toggle_drag()
4308 {
4309         return toggle_drag;
4310 }
4311
4312 int BC_WindowBase::set_icon(VFrame *data)
4313 {
4314         if(icon_pixmap) delete icon_pixmap;
4315         icon_pixmap = new BC_Pixmap(top_level, data, PIXMAP_ALPHA, 1);
4316
4317         if(icon_window) delete icon_window;
4318         icon_window = new BC_Popup(this,
4319                 (int)BC_INFINITY,
4320                 (int)BC_INFINITY,
4321                 icon_pixmap->get_w(),
4322                 icon_pixmap->get_h(),
4323                 -1,
4324                 1, // All windows are hidden initially
4325                 icon_pixmap);
4326
4327         XWMHints wm_hints;
4328         wm_hints.flags = WindowGroupHint | IconPixmapHint | IconMaskHint | IconWindowHint;
4329         wm_hints.icon_pixmap = icon_pixmap->get_pixmap();
4330         wm_hints.icon_mask = icon_pixmap->get_alpha();
4331         wm_hints.icon_window = icon_window->win;
4332         wm_hints.window_group = XGroupLeader;
4333
4334 // for(int i = 0; i < 1000; i++)
4335 // printf("02x ", icon_pixmap->get_alpha()->get_row_pointers()[0][i]);
4336 // printf("\n");
4337
4338         XSetWMHints(top_level->display, top_level->win, &wm_hints);
4339         XSync(top_level->display, 0);
4340         return 0;
4341 }
4342
4343 void BC_WindowBase::init_resources(float scale)
4344 {
4345         if( resources ) return;
4346         XInitThreads();
4347         const char *env = getenv("BC_SCALE");
4348         if( env ) scale = atof(env);
4349         float x_scale = 1, y_scale = 1;
4350         if( scale <= 0 ) {
4351                 BC_DisplayInfo info;
4352                 int wx, wy, ww, wh;
4353                 int cins = info.xinerama_big_screen();
4354                 if( !info.xinerama_geometry(cins, wx, wy, ww, wh) ) {
4355                         if( (x_scale = ww/1920.) < 1 ) x_scale = 1;
4356                         if( (y_scale = wh/1080.) < 1 ) y_scale = 1;
4357                 }
4358         }
4359         else
4360                 x_scale = y_scale = scale;
4361         // constructor sets BC_WindowBase::resources
4362         new BC_Resources(x_scale, y_scale);
4363 }
4364 void BC_WindowBase::finit_resources()
4365 {
4366         delete resources;  resources = 0;
4367 }
4368
4369 int BC_WindowBase::set_w(int w)
4370 {
4371         this->w = w;
4372         return 0;
4373 }
4374
4375 int BC_WindowBase::set_h(int h)
4376 {
4377         this->h = h;
4378         return 0;
4379 }
4380
4381 int BC_WindowBase::load_defaults(BC_Hash *defaults)
4382 {
4383         char string[BCTEXTLEN];
4384         int newest_id = - 1;
4385         for(int i = 0; i < FILEBOX_HISTORY_SIZE; i++)
4386         {
4387                 sprintf(string, "FILEBOX_HISTORY_PATH%d", i);
4388                 resources->filebox_history[i].path[0] = 0;
4389                 defaults->get(string, resources->filebox_history[i].path);
4390                 sprintf(string, "FILEBOX_HISTORY_ID%d", i);
4391                 resources->filebox_history[i].id = defaults->get(string, resources->get_id());
4392                 if(resources->filebox_history[i].id > newest_id)
4393                         newest_id = resources->filebox_history[i].id;
4394         }
4395
4396         resources->filebox_id = newest_id + 1;
4397         resources->filebox_mode = defaults->get("FILEBOX_MODE", get_resources()->filebox_mode);
4398         resources->filebox_w = defaults->get("FILEBOX_W", get_resources()->filebox_w);
4399         resources->filebox_h = defaults->get("FILEBOX_H", get_resources()->filebox_h);
4400         resources->filebox_columntype[0] = defaults->get("FILEBOX_TYPE0", resources->filebox_columntype[0]);
4401         resources->filebox_columntype[1] = defaults->get("FILEBOX_TYPE1", resources->filebox_columntype[1]);
4402         resources->filebox_columntype[2] = defaults->get("FILEBOX_TYPE2", resources->filebox_columntype[2]);
4403         resources->filebox_columntype[3] = defaults->get("FILEBOX_TYPE3", resources->filebox_columntype[3]);
4404         resources->filebox_columnwidth[0] = defaults->get("FILEBOX_WIDTH0", resources->filebox_columnwidth[0]);
4405         resources->filebox_columnwidth[1] = defaults->get("FILEBOX_WIDTH1", resources->filebox_columnwidth[1]);
4406         resources->filebox_columnwidth[2] = defaults->get("FILEBOX_WIDTH2", resources->filebox_columnwidth[2]);
4407         resources->filebox_columnwidth[3] = defaults->get("FILEBOX_WIDTH3", resources->filebox_columnwidth[3]);
4408         resources->filebox_size_format = defaults->get("FILEBOX_SIZE_FORMAT", get_resources()->filebox_size_format);
4409         defaults->get("FILEBOX_FILTER", resources->filebox_filter);
4410         return 0;
4411 }
4412
4413 int BC_WindowBase::save_defaults(BC_Hash *defaults)
4414 {
4415         char string[BCTEXTLEN];
4416         for(int i = 0; i < FILEBOX_HISTORY_SIZE; i++)
4417         {
4418                 sprintf(string, "FILEBOX_HISTORY_PATH%d", i);
4419                 defaults->update(string, resources->filebox_history[i].path);
4420                 sprintf(string, "FILEBOX_HISTORY_ID%d", i);
4421                 defaults->update(string, resources->filebox_history[i].id);
4422         }
4423         defaults->update("FILEBOX_MODE", resources->filebox_mode);
4424         defaults->update("FILEBOX_W", resources->filebox_w);
4425         defaults->update("FILEBOX_H", resources->filebox_h);
4426         defaults->update("FILEBOX_TYPE0", resources->filebox_columntype[0]);
4427         defaults->update("FILEBOX_TYPE1", resources->filebox_columntype[1]);
4428         defaults->update("FILEBOX_TYPE2", resources->filebox_columntype[2]);
4429         defaults->update("FILEBOX_TYPE3", resources->filebox_columntype[3]);
4430         defaults->update("FILEBOX_WIDTH0", resources->filebox_columnwidth[0]);
4431         defaults->update("FILEBOX_WIDTH1", resources->filebox_columnwidth[1]);
4432         defaults->update("FILEBOX_WIDTH2", resources->filebox_columnwidth[2]);
4433         defaults->update("FILEBOX_WIDTH3", resources->filebox_columnwidth[3]);
4434         defaults->update("FILEBOX_FILTER", resources->filebox_filter);
4435         defaults->update("FILEBOX_SIZE_FORMAT", get_resources()->filebox_size_format);
4436         return 0;
4437 }
4438
4439
4440
4441 // For some reason XTranslateCoordinates can take a long time to return.
4442 // We work around this by only calling it when the event windows are different.
4443 void BC_WindowBase::translate_coordinates(Window src_w, Window dest_w,
4444                 int src_x, int src_y, int *dest_x_return, int *dest_y_return)
4445 {
4446         Window tempwin = 0;
4447 //Timer timer;
4448 //timer.update();
4449         if(src_w == dest_w)
4450         {
4451                 *dest_x_return = src_x;
4452                 *dest_y_return = src_y;
4453         }
4454         else
4455         {
4456                 XTranslateCoordinates(top_level->display, src_w, dest_w,
4457                         src_x, src_y, dest_x_return, dest_y_return, &tempwin);
4458 //printf("BC_WindowBase::translate_coordinates 1 %lld\n", timer.get_difference());
4459         }
4460 }
4461
4462 void BC_WindowBase::get_root_coordinates(int x, int y, int *abs_x, int *abs_y)
4463 {
4464         translate_coordinates(win, top_level->rootwin, x, y, abs_x, abs_y);
4465 }
4466
4467 void BC_WindowBase::get_win_coordinates(int abs_x, int abs_y, int *x, int *y)
4468 {
4469         translate_coordinates(top_level->rootwin, win, abs_x, abs_y, x, y);
4470 }
4471
4472
4473 #ifdef HAVE_LIBXXF86VM
4474 void BC_WindowBase::closest_vm(int *vm, int *width, int *height)
4475 {
4476         int foo,bar;
4477         *vm = 0;
4478         if(XF86VidModeQueryExtension(top_level->display,&foo,&bar)) {
4479                 int vm_count,i;
4480                 XF86VidModeModeInfo **vm_modelines;
4481                 XF86VidModeGetAllModeLines(top_level->display,
4482                         XDefaultScreen(top_level->display), &vm_count,&vm_modelines);
4483                 for( i = 0; i < vm_count; i++ ) {
4484                         if( vm_modelines[i]->hdisplay < vm_modelines[*vm]->hdisplay &&
4485                             vm_modelines[i]->hdisplay >= *width )
4486                                 *vm = i;
4487                 }
4488                 display = top_level->display;
4489                 if( vm_modelines[*vm]->hdisplay == *width )
4490                         *vm = -1;
4491                 else {
4492                         *width = vm_modelines[*vm]->hdisplay;
4493                         *height = vm_modelines[*vm]->vdisplay;
4494                 }
4495         }
4496 }
4497
4498 void BC_WindowBase::scale_vm(int vm)
4499 {
4500         int foo,bar,dotclock;
4501         if( XF86VidModeQueryExtension(top_level->display,&foo,&bar) ) {
4502                 int vm_count;
4503                 XF86VidModeModeInfo **vm_modelines;
4504                 XF86VidModeModeLine vml;
4505                 XF86VidModeGetAllModeLines(top_level->display,
4506                         XDefaultScreen(top_level->display), &vm_count,&vm_modelines);
4507                 XF86VidModeGetModeLine(top_level->display,
4508                         XDefaultScreen(top_level->display), &dotclock,&vml);
4509                 orig_modeline.dotclock = dotclock;
4510                 orig_modeline.hdisplay = vml.hdisplay;
4511                 orig_modeline.hsyncstart = vml.hsyncstart;
4512                 orig_modeline.hsyncend = vml.hsyncend;
4513                 orig_modeline.htotal = vml.htotal;
4514                 orig_modeline.vdisplay = vml.vdisplay;
4515                 orig_modeline.vsyncstart = vml.vsyncstart;
4516                 orig_modeline.vsyncend = vml.vsyncend;
4517                 orig_modeline.vtotal = vml.vtotal;
4518                 orig_modeline.flags = vml.flags;
4519                 orig_modeline.privsize = vml.privsize;
4520                 // orig_modeline.private = vml.private;
4521                 XF86VidModeSwitchToMode(top_level->display,XDefaultScreen(top_level->display),vm_modelines[vm]);
4522                 XF86VidModeSetViewPort(top_level->display,XDefaultScreen(top_level->display),0,0);
4523                 XFlush(top_level->display);
4524         }
4525 }
4526
4527 void BC_WindowBase::restore_vm()
4528 {
4529         XF86VidModeSwitchToMode(top_level->display,XDefaultScreen(top_level->display),&orig_modeline);
4530         XFlush(top_level->display);
4531 }
4532 #endif
4533
4534
4535 #ifndef SINGLE_THREAD
4536 int BC_WindowBase::get_event_count()
4537 {
4538         event_lock->lock("BC_WindowBase::get_event_count");
4539         int result = common_events.total;
4540         event_lock->unlock();
4541         return result;
4542 }
4543
4544 XEvent* BC_WindowBase::get_event()
4545 {
4546         XEvent *result = 0;
4547         while(!done && !result)
4548         {
4549                 event_condition->lock("BC_WindowBase::get_event");
4550                 event_lock->lock("BC_WindowBase::get_event");
4551
4552                 if(common_events.total && !done)
4553                 {
4554                         result = common_events.values[0];
4555                         common_events.remove_number(0);
4556                 }
4557
4558                 event_lock->unlock();
4559         }
4560         return result;
4561 }
4562
4563 void BC_WindowBase::put_event(XEvent *event)
4564 {
4565         event_lock->lock("BC_WindowBase::put_event");
4566         common_events.append(event);
4567         event_lock->unlock();
4568         event_condition->unlock();
4569 }
4570
4571 void BC_WindowBase::dequeue_events(Window win)
4572 {
4573         event_lock->lock("BC_WindowBase::dequeue_events");
4574
4575         int out = 0, total = common_events.size();
4576         for( int in=0; in<total; ++in ) {
4577                 if( common_events[in]->xany.window == win ) continue;
4578                 common_events[out++] = common_events[in];
4579         }
4580         common_events.total = out;
4581
4582         event_lock->unlock();
4583 }
4584
4585 int BC_WindowBase::resend_event(BC_WindowBase *window)
4586 {
4587         if( resend_event_window ) return 1;
4588         resend_event_window = window;
4589         return 0;
4590 }
4591
4592 #else
4593
4594 int BC_WindowBase::resend_event(BC_WindowBase *window)
4595 {
4596         return 1;
4597 }
4598
4599 #endif // SINGLE_THREAD
4600
4601 int BC_WindowBase::get_id()
4602 {
4603         return id;
4604 }
4605
4606
4607 BC_Pixmap *BC_WindowBase::create_pixmap(VFrame *vframe)
4608 {
4609         int w = vframe->get_w(), h = vframe->get_h();
4610         BC_Pixmap *icon = new BC_Pixmap(this, w, h);
4611         icon->draw_vframe(vframe, 0,0, w,h, 0,0);
4612         return icon;
4613 }
4614
4615
4616 void BC_WindowBase::flicker(int n, int ms)
4617 {
4618         int color = get_bg_color();
4619         for( int i=2*n; --i>=0; ) {
4620                 set_inverse();          set_bg_color(WHITE);
4621                 clear_box(0,0, w,h);    flash(1);
4622                 sync_display();         Timer::delay(ms);
4623         }
4624         set_bg_color(color);
4625         set_opaque();
4626 }
4627
4628 void BC_WindowBase::focus()
4629 {
4630         XWindowAttributes xwa;
4631         XGetWindowAttributes(top_level->display, top_level->win, &xwa);
4632         if( xwa.map_state == IsViewable )
4633                 XSetInputFocus(top_level->display, top_level->win, RevertToParent, CurrentTime);
4634 }
4635