rest of Termux related mods from Andrew
[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 #ifdef HAVE_XV
61 #include <X11/extensions/Xvlib.h>
62 #endif
63 #include <X11/extensions/shape.h>
64 #include <X11/XF86keysym.h>
65 #include <X11/Sunkeysym.h>
66
67 BC_ResizeCall::BC_ResizeCall(int w, int h)
68 {
69         this->w = w;
70         this->h = h;
71 }
72
73
74 int BC_WindowBase::shm_completion_event = -1;
75 BC_Resources *BC_WindowBase::resources = 0;
76 Window XGroupLeader = 0;
77
78 Mutex BC_KeyboardHandlerLock::keyboard_listener_mutex("keyboard_listener",0);
79 ArrayList<BC_KeyboardHandler*> BC_KeyboardHandler::listeners;
80
81 BC_WindowBase::BC_WindowBase()
82 {
83 //printf("BC_WindowBase::BC_WindowBase 1\n");
84         BC_WindowBase::initialize();
85 }
86
87 BC_WindowBase::~BC_WindowBase()
88 {
89 #ifdef SINGLE_THREAD
90         BC_Display::lock_display("BC_WindowBase::~BC_WindowBase");
91 #else
92         if(window_type == MAIN_WINDOW)
93                 lock_window("BC_WindowBase::~BC_WindowBase");
94 #endif
95
96 #ifdef HAVE_LIBXXF86VM
97         if(window_type == VIDMODE_SCALED_WINDOW && vm_switched) {
98                 restore_vm();
99         }
100 #endif
101         is_deleting = 1;
102
103         hide_tooltip();
104         if(window_type != MAIN_WINDOW)
105         {
106 // stop event input
107                 XSelectInput(top_level->display, this->win, 0);
108                 XSync(top_level->display,0);
109 #ifndef SINGLE_THREAD
110                 top_level->dequeue_events(win);
111 #endif
112 // drop active window refs to this
113                 if(top_level->active_menubar == this) top_level->active_menubar = 0;
114                 if(top_level->active_popup_menu == this) top_level->active_popup_menu = 0;
115                 if(top_level->active_subwindow == this) top_level->active_subwindow = 0;
116 // drop motion window refs to this
117                 if(top_level->motion_events && top_level->last_motion_win == this->win)
118                         top_level->motion_events = 0;
119
120 // Remove pointer from parent window to this
121                 parent_window->subwindows->remove(this);
122         }
123
124         if(grab_active) grab_active->active_grab = 0;
125         if(icon_window) delete icon_window;
126         if(window_type == POPUP_WINDOW)
127                 parent_window->remove_popup(this);
128
129 // Delete the subwindows
130         if(subwindows)
131         {
132                 while(subwindows->total)
133                 {
134 // Subwindow removes its own pointer
135                         delete subwindows->values[0];
136                 }
137                 delete subwindows;
138         }
139
140         delete pixmap;
141
142 //printf("delete glx=%08x, win=%08x %s\n", (unsigned)glx_win, (unsigned)win, title);
143 #ifdef HAVE_GL
144         if( get_resources()->get_synchronous() && glx_win != 0 ) {
145                 if( window_type == MAIN_WINDOW )
146                         unlock_window();
147                 get_resources()->get_synchronous()->delete_window(this);
148                 if( window_type == MAIN_WINDOW )
149                         lock_window("BC_WindowBase::delete_window");
150         }
151 #endif
152         XDestroyWindow(top_level->display, win);
153
154         if(bg_pixmap && !shared_bg_pixmap) delete bg_pixmap;
155         if(icon_pixmap) delete icon_pixmap;
156         if(temp_bitmap) delete temp_bitmap;
157         top_level->active_bitmaps.remove_buffers(this);
158         if(_7segment_pixmaps)
159         {
160                 for(int i = 0; i < TOTAL_7SEGMENT; i++)
161                         delete _7segment_pixmaps[i];
162
163                 delete [] _7segment_pixmaps;
164         }
165
166
167
168         if(window_type == MAIN_WINDOW)
169         {
170                 XFreeGC(display, gc);
171                 static XFontStruct *BC_WindowBase::*xfont[] = {
172                          &BC_WindowBase::smallfont,
173                          &BC_WindowBase::mediumfont,
174                          &BC_WindowBase::largefont,
175                          &BC_WindowBase::bigfont,
176                          &BC_WindowBase::clockfont,
177                 };
178                 for( int i=sizeof(xfont)/sizeof(xfont[0]); --i>=0; )
179                         XFreeFont(display, this->*xfont[i]);
180
181 #ifdef HAVE_XFT
182 // prevents a bug when Xft closes with unrefd fonts
183                 FcPattern *defaults = FcPatternCreate();
184                 FcPatternAddInteger(defaults, "maxunreffonts", 0);
185                 XftDefaultSet(display, defaults);
186
187                 static void *BC_WindowBase::*xft_font[] = {
188                          &BC_WindowBase::smallfont_xft,
189                          &BC_WindowBase::mediumfont_xft,
190                          &BC_WindowBase::largefont_xft,
191                          &BC_WindowBase::bigfont_xft,
192                          &BC_WindowBase::bold_smallfont_xft,
193                          &BC_WindowBase::bold_mediumfont_xft,
194                          &BC_WindowBase::bold_largefont_xft,
195                          &BC_WindowBase::clockfont_xft,
196                 };
197                 for( int i=sizeof(xft_font)/sizeof(xft_font[0]); --i>=0; ) {
198                         XftFont *xft = (XftFont *)(this->*xft_font[i]);
199                         if( xft ) xftFontClose (display, xft);
200                 }
201 #endif
202                 finit_im();
203                 flush();
204                 sync_display();
205
206                 if( xinerama_info )
207                         XFree(xinerama_info);
208                 xinerama_screens = 0;
209                 xinerama_info = 0;
210 #ifdef HAVE_XV
211                 if( xvideo_port_id >= 0 )
212                         XvUngrabPort(display, xvideo_port_id, CurrentTime);
213 #endif
214                 unlock_window();
215 // Must be last reference to display.
216 // _XftDisplayInfo needs a lock.
217                 get_resources()->create_window_lock->lock("BC_WindowBase::~BC_WindowBase");
218                 XCloseDisplay(display);
219                 get_resources()->create_window_lock->unlock();
220
221 // clipboard uses a different display connection
222                 clipboard->stop_clipboard();
223                 delete clipboard;
224         }
225
226         resize_history.remove_all_objects();
227         delete grab_lock;
228
229 #ifndef SINGLE_THREAD
230         common_events.remove_all_objects();
231         delete event_lock;
232         delete event_condition;
233         delete init_lock;
234 #else
235         top_level->window_lock = 0;
236         BC_Display::unlock_display();
237 #endif
238         delete cursor_timer;
239
240 #if HAVE_GL
241         if( glx_fbcfgs_window ) XFree(glx_fbcfgs_window);
242         if( glx_fbcfgs_pbuffer) XFree(glx_fbcfgs_pbuffer);
243         if( glx_fbcfgs_pixmap ) XFree(glx_fbcfgs_pixmap);
244 #endif
245
246         UNSET_ALL_LOCKS(this)
247 }
248
249 int BC_WindowBase::initialize()
250 {
251         done = 0;
252         done_set = 0;
253         window_running = 0;
254         display_lock_owner = 0;
255         test_keypress = 0;
256         keys_return[0] = 0;
257         context_help_keyword[0] = 0;
258         is_deleting = 0;
259         window_lock = 0;
260         resend_event_window = 0;
261         x = 0;
262         y = 0;
263         w = 0;
264         h = 0;
265         bg_color = -1;
266         line_width = 1;
267         line_dashes = 0;
268         top_level = 0;
269         parent_window = 0;
270         subwindows = 0;
271         xinerama_info = 0;
272         xinerama_screens = 0;
273         xvideo_port_id = -1;
274         video_on = 0;
275         motion_events = 0;
276         resize_events = 0;
277         translation_events = 0;
278         ctrl_mask = shift_mask = alt_mask = 0;
279         cursor_x = cursor_y = button_number = 0;
280         button_down = 0;
281         button_pressed = 0;
282         button_time1 = 0;
283         button_time2 = 0;
284         button_time3 = 0;
285         double_click = 0;
286         triple_click = 0;
287         event_win = 0;
288         last_motion_win = 0;
289         key_pressed = 0;
290         active_grab = 0;
291         grab_active = 0;
292         active_menubar = 0;
293         active_popup_menu = 0;
294         active_subwindow = 0;
295         cursor_entered = 0;
296         pixmap = 0;
297         bg_pixmap = 0;
298         _7segment_pixmaps = 0;
299         tooltip_text = 0;
300         force_tooltip = 0;
301 //      next_repeat_id = 0;
302         tooltip_popup = 0;
303         current_font = MEDIUMFONT;
304         current_color = BLACK;
305         current_cursor = ARROW_CURSOR;
306         hourglass_total = 0;
307         is_dragging = 0;
308         shared_bg_pixmap = 0;
309         icon_pixmap = 0;
310         icon_window = 0;
311         window_type = MAIN_WINDOW;
312         translation_count = 0;
313         x_correction = y_correction = 0;
314         temp_bitmap = 0;
315         tooltip_on = 0;
316         temp_cursor = 0;
317         toggle_value = 0;
318         toggle_drag = 0;
319         has_focus = 0;
320         is_hourglass = 0;
321         is_transparent = 0;
322 #ifdef HAVE_LIBXXF86VM
323         vm_switched = 0;
324 #endif
325         input_method = 0;
326         input_context = 0;
327
328         smallfont = 0;
329         mediumfont = 0;
330         largefont = 0;
331         bigfont = 0;
332         clockfont = 0;
333
334         smallfont_xft = 0;
335         mediumfont_xft = 0;
336         largefont_xft = 0;
337         bigfont_xft = 0;
338         clockfont_xft = 0;
339
340         bold_smallfont_xft = 0;
341         bold_mediumfont_xft = 0;
342         bold_largefont_xft = 0;
343 #ifdef SINGLE_THREAD
344         completion_lock = new Condition(0, "BC_WindowBase::completion_lock");
345 #else
346 // Need these right away since put_event is called before run_window sometimes.
347         event_lock = new Mutex("BC_WindowBase::event_lock");
348         event_condition = new Condition(0, "BC_WindowBase::event_condition");
349         init_lock = new Condition(0, "BC_WindowBase::init_lock");
350 #endif
351         grab_lock = new Mutex("BC_WindowBase::grab_lock");
352
353         cursor_timer = new Timer;
354         event_thread = 0;
355 #ifdef HAVE_GL
356         glx_fbcfgs_window = 0;  n_fbcfgs_window = 0;
357         glx_fbcfgs_pbuffer = 0; n_fbcfgs_pbuffer = 0;
358         glx_fbcfgs_pixmap = 0;  n_fbcfgs_pixmap = 0;
359
360         glx_fb_config = 0;
361         glx_win_context = 0;
362         glx_win = 0;
363 #endif
364
365         flash_enabled = 1;
366         win = 0;
367         return 0;
368 }
369
370
371
372 #define DEFAULT_EVENT_MASKS EnterWindowMask | \
373                         LeaveWindowMask | \
374                         ButtonPressMask | \
375                         ButtonReleaseMask | \
376                         PointerMotionMask | \
377                         FocusChangeMask
378
379
380 int BC_WindowBase::create_window(BC_WindowBase *parent_window, const char *title,
381                 int x, int y, int w, int h, int minw, int minh, int allow_resize,
382                 int private_color, int hide, int bg_color, const char *display_name,
383                 int window_type, BC_Pixmap *bg_pixmap, int group_it)
384 {
385         XSetWindowAttributes attr;
386         unsigned long mask;
387         XSizeHints size_hints;
388         int root_w;
389         int root_h;
390 #ifdef HAVE_LIBXXF86VM
391         int vm;
392 #endif
393
394         id = get_resources()->get_id();
395         if(parent_window) top_level = parent_window->top_level;
396         if( top_level ) lock_window("BC_WindowBase::create_window");
397         get_resources()->create_window_lock->lock("BC_WindowBase::create_window");
398
399 #ifdef HAVE_LIBXXF86VM
400         if(window_type == VIDMODE_SCALED_WINDOW)
401                 closest_vm(&vm,&w,&h);
402 #endif
403
404         this->x = x;
405         this->y = y;
406         this->w = w;
407         this->h = h;
408         this->bg_color = bg_color;
409         this->window_type = window_type;
410         this->hidden = hide;
411         this->private_color = private_color;
412         this->parent_window = parent_window;
413         this->bg_pixmap = bg_pixmap;
414         this->allow_resize = allow_resize;
415         if(display_name)
416                 strcpy(this->display_name, display_name);
417         else
418                 this->display_name[0] = 0;
419
420         put_title(title);
421         if(bg_pixmap) shared_bg_pixmap = 1;
422
423         subwindows = new BC_SubWindowList;
424
425         if(window_type == MAIN_WINDOW)
426         {
427                 top_level = this;
428                 parent_window = this;
429
430
431 #ifdef SINGLE_THREAD
432                 display = BC_Display::get_display(display_name);
433                 BC_Display::lock_display("BC_WindowBase::create_window");
434 //              BC_Display::display_global->new_window(this);
435 #else
436
437 // get the display connection
438
439 // This function must be the first Xlib
440 // function a multi-threaded program calls
441                 XInitThreads();
442                 display = init_display(display_name);
443                 if( shm_completion_event < 0 ) shm_completion_event =
444                         ShmCompletion + XShmGetEventBase(display);
445 #endif
446                 lock_window("BC_WindowBase::create_window 1");
447
448                 screen = DefaultScreen(display);
449                 rootwin = RootWindow(display, screen);
450 // window placement boundaries
451                 if( !xinerama_screens && XineramaIsActive(display) )
452                         xinerama_info = XineramaQueryScreens(display, &xinerama_screens);
453                 root_w = get_root_w(0);
454                 root_h = get_root_h(0);
455
456 #if HAVE_GL
457                 vis = get_glx_visual(display);
458                 if( !vis )
459 #endif
460                 {
461                         int mask = VisualDepthMask | VisualClassMask;
462                         static XVisualInfo vinfo;
463                         memset(&vinfo, 0, sizeof(vinfo));
464                         vinfo.depth = 24;
465                         vinfo.c_class = TrueColor;
466                         int nitems = 0;
467                         XVisualInfo *vis_info = XGetVisualInfo(display, mask, &vinfo, &nitems);
468                         vis = vis_info && nitems>0 ? vis_info[0].visual : 0;
469                         if( vis_info ) XFree(vis_info);
470                 }
471                 if( !vis )
472                         vis = DefaultVisual(display, screen);
473                 default_depth = DefaultDepth(display, screen);
474
475                 client_byte_order = (*(const u_int32_t*)"a   ") & 0x00000001;
476                 server_byte_order = (XImageByteOrder(display) == MSBFirst) ? 0 : 1;
477
478
479 // This must be done before fonts to know if antialiasing is available.
480                 init_colors();
481 // get the resources
482                 if(resources->use_shm < 0) resources->initialize_display(this);
483                 x_correction = BC_DisplayInfo::get_left_border();
484                 y_correction = BC_DisplayInfo::get_top_border();
485
486 // clamp window placement
487                 if(this->x + this->w + x_correction > root_w)
488                         this->x = root_w - this->w - x_correction;
489                 if(this->y + this->h + y_correction > root_h)
490                         this->y = root_h - this->h - y_correction;
491                 if(this->x < 0) this->x = 0;
492                 if(this->y < 0) this->y = 0;
493
494                 if(this->bg_color == -1)
495                         this->bg_color = resources->get_bg_color();
496
497 // printf("bcwindowbase 1 %s\n", title);
498 // if(window_type == MAIN_WINDOW) sleep(1);
499 // printf("bcwindowbase 10\n");
500                 init_fonts();
501                 init_gc();
502                 init_cursors();
503
504 // Create the window
505                 mask = CWEventMask | CWBackPixel | CWColormap | CWCursor;
506
507                 attr.event_mask = DEFAULT_EVENT_MASKS |
508                         StructureNotifyMask |
509                         KeyPressMask |
510                         KeyReleaseMask;
511
512                 attr.background_pixel = get_color(this->bg_color);
513                 attr.colormap = cmap;
514                 attr.cursor = get_cursor_struct(ARROW_CURSOR);
515
516                 win = XCreateWindow(display, rootwin,
517                         this->x, this->y, this->w, this->h, 0,
518                         top_level->default_depth, InputOutput,
519                         vis, mask, &attr);
520                 XGetNormalHints(display, win, &size_hints);
521
522                 size_hints.flags = PSize | PMinSize | PMaxSize;
523                 size_hints.width = this->w;
524                 size_hints.height = this->h;
525                 size_hints.min_width = allow_resize ? minw : this->w;
526                 size_hints.max_width = allow_resize ? 32767 : this->w;
527                 size_hints.min_height = allow_resize ? minh : this->h;
528                 size_hints.max_height = allow_resize ? 32767 : this->h;
529                 if(x > -BC_INFINITY && x < BC_INFINITY)
530                 {
531                         size_hints.flags |= PPosition;
532                         size_hints.x = this->x;
533                         size_hints.y = this->y;
534                 }
535                 XSetWMProperties(display, win, 0, 0, 0, 0, &size_hints, 0, 0);
536                 get_atoms();
537                 set_title(title);
538 #ifndef SINGLE_THREAD
539                 clipboard = new BC_Clipboard(this);
540                 clipboard->start_clipboard();
541 #endif
542
543
544                 if (group_it)
545                 {
546                         Atom ClientLeaderXAtom;
547                         if (XGroupLeader == 0)
548                                 XGroupLeader = win;
549                         const char *instance_name = "cinelerra";
550                         const char *class_name = "Cinelerra";
551                         XClassHint *class_hints = XAllocClassHint();
552                         class_hints->res_name = (char*)instance_name;
553                         class_hints->res_class = (char*)class_name;
554                         XSetClassHint(top_level->display, win, class_hints);
555                         XFree(class_hints);
556                         ClientLeaderXAtom = XInternAtom(display, "WM_CLIENT_LEADER", True);
557                         XChangeProperty(display, win, ClientLeaderXAtom, XA_WINDOW, 32,
558                                 PropModeReplace, (unsigned char *)&XGroupLeader, true);
559                 }
560                 init_im();
561                 set_icon(get_resources()->default_icon);
562         }
563
564 #ifdef HAVE_LIBXXF86VM
565         if(window_type == VIDMODE_SCALED_WINDOW && vm != -1)
566         {
567                 scale_vm (vm);
568                 vm_switched = 1;
569         }
570 #endif
571
572 #ifdef HAVE_LIBXXF86VM
573         if(window_type == POPUP_WINDOW || window_type == VIDMODE_SCALED_WINDOW)
574 #else
575         if(window_type == POPUP_WINDOW)
576 #endif
577         {
578                 mask = CWEventMask | CWBackPixel | CWColormap |
579                         CWOverrideRedirect | CWSaveUnder | CWCursor;
580
581                 attr.event_mask = DEFAULT_EVENT_MASKS | ExposureMask |
582                         KeyPressMask | KeyReleaseMask;
583
584                 if(this->bg_color == -1)
585                         this->bg_color = resources->get_bg_color();
586                 attr.background_pixel = top_level->get_color(bg_color);
587                 attr.colormap = top_level->cmap;
588                 if(top_level->is_hourglass)
589                         attr.cursor = top_level->get_cursor_struct(HOURGLASS_CURSOR);
590                 else
591                         attr.cursor = top_level->get_cursor_struct(ARROW_CURSOR);
592                 attr.override_redirect = True;
593                 attr.save_under = True;
594
595                 win = XCreateWindow(top_level->display,
596                         top_level->rootwin, this->x, this->y, this->w, this->h, 0,
597                         top_level->default_depth, InputOutput, top_level->vis, mask,
598                         &attr);
599                 top_level->add_popup(this);
600         }
601
602         if(window_type == SUB_WINDOW)
603         {
604                 mask = CWEventMask | CWBackPixel | CWCursor;
605                 attr.event_mask = DEFAULT_EVENT_MASKS;
606                 attr.background_pixel = top_level->get_color(this->bg_color);
607                 if(top_level->is_hourglass)
608                         attr.cursor = top_level->get_cursor_struct(HOURGLASS_CURSOR);
609                 else
610                         attr.cursor = top_level->get_cursor_struct(ARROW_CURSOR);
611                 win = XCreateWindow(top_level->display,
612                         parent_window->win, this->x, this->y, this->w, this->h, 0,
613                         top_level->default_depth, InputOutput, top_level->vis, mask,
614                         &attr);
615                 init_window_shape();
616                 if(!hidden) XMapWindow(top_level->display, win);
617         }
618
619 // Create pixmap for all windows
620         pixmap = new BC_Pixmap(this, this->w, this->h);
621
622 // Set up options for main window
623         if(window_type == MAIN_WINDOW)
624         {
625                 if(get_resources()->bg_image && !bg_pixmap && bg_color < 0)
626                 {
627                         this->bg_pixmap = new BC_Pixmap(this,
628                                 get_resources()->bg_image,
629                                 PIXMAP_OPAQUE);
630                 }
631
632                 if(!hidden) show_window();
633                 init_glyphs();
634         }
635
636         draw_background(0, 0, this->w, this->h);
637
638         flash(-1, -1, -1, -1, 0);
639
640 // Set up options for popup window
641 #ifdef HAVE_LIBXXF86VM
642         if(window_type == POPUP_WINDOW || window_type == VIDMODE_SCALED_WINDOW)
643 #else
644         if(window_type == POPUP_WINDOW)
645 #endif
646         {
647                 init_window_shape();
648                 if(!hidden) show_window();
649         }
650         get_resources()->create_window_lock->unlock();
651         unlock_window();
652
653         return 0;
654 }
655
656 Display* BC_WindowBase::init_display(const char *display_name)
657 {
658         Display* display;
659
660         if(display_name && display_name[0] == 0) display_name = NULL;
661         if((display = XOpenDisplay(display_name)) == NULL) {
662                 printf("BC_WindowBase::init_display: cannot connect to X server %s\n",
663                         display_name);
664                 if(getenv("DISPLAY") == NULL) {
665                         printf(_("'DISPLAY' environment variable not set.\n"));
666                         exit(1);
667                 }
668 // Try again with default display.
669                 if((display = XOpenDisplay(0)) == NULL) {
670                         printf("BC_WindowBase::init_display: cannot connect to default X server.\n");
671                         exit(1);
672                 }
673         }
674
675         static int xsynch = -1;
676         if( xsynch < 0 ) {
677                 const char *cp = getenv("CIN_XSYNCH");
678                 xsynch = !cp ? 0 : atoi(cp);
679         }
680         if( xsynch > 0 )
681                 XSynchronize(display, True);
682
683         return display;
684 }
685
686 Display* BC_WindowBase::get_display()
687 {
688         return top_level->display;
689 }
690
691 int BC_WindowBase::get_screen()
692 {
693         return top_level->screen;
694 }
695
696 int BC_WindowBase::run_window()
697 {
698         done_set = done = 0;
699         return_value = 0;
700
701
702 // Events may have been sent before run_window so can't initialize them here.
703
704 #ifdef SINGLE_THREAD
705         set_repeat(get_resources()->tooltip_delay);
706         BC_Display::display_global->new_window(this);
707
708 // If the first window created, run the display loop in this thread.
709         if(BC_Display::display_global->is_first(this))
710         {
711                 BC_Display::unlock_display();
712                 BC_Display::display_global->loop();
713         }
714         else
715         {
716                 BC_Display::unlock_display();
717                 completion_lock->lock("BC_WindowBase::run_window");
718         }
719
720         BC_Display::lock_display("BC_WindowBase::run_window");
721         BC_Display::display_global->delete_window(this);
722
723         unset_all_repeaters();
724         hide_tooltip();
725         BC_Display::unlock_display();
726
727 #else // SINGLE_THREAD
728
729
730
731 // Start tooltips
732         set_repeat(get_resources()->tooltip_delay);
733
734 // Start X server events
735         event_thread = new BC_WindowEvents(this);
736         event_thread->start();
737
738 // Release wait lock
739         window_running = 1;
740         init_lock->unlock();
741
742 // Handle common events
743         while( !done ) {
744                 dispatch_event();
745         }
746
747         unset_all_repeaters();
748         hide_tooltip();
749         delete event_thread;
750         event_thread = 0;
751         event_condition->reset();
752         common_events.remove_all_objects();
753         window_running = 0;
754         done = 0;
755
756 #endif // SINGLE_THREAD
757
758         return return_value;
759 }
760
761 int BC_WindowBase::get_key_masks(unsigned int key_state)
762 {
763 // printf("BC_WindowBase::get_key_masks %llx\n",
764 // event->xkey.state);
765         ctrl_mask = (key_state & ControlMask) ? 1 : 0;  // ctrl key down
766         shift_mask = (key_state & ShiftMask) ? 1 : 0;   // shift key down
767         alt_mask = (key_state & Mod1Mask) ? 1 : 0;      // alt key down
768         return 0;
769 }
770
771
772 void BC_WindowBase::add_keyboard_listener(int(BC_WindowBase::*handler)(BC_WindowBase *))
773 {
774         BC_KeyboardHandlerLock set;
775         BC_KeyboardHandler::listeners.append(new BC_KeyboardHandler(handler, this));
776 }
777
778 void BC_WindowBase::del_keyboard_listener(int(BC_WindowBase::*handler)(BC_WindowBase *))
779 {
780         BC_KeyboardHandlerLock set;
781         int i = BC_KeyboardHandler::listeners.size();
782         while( --i >= 0 && BC_KeyboardHandler::listeners[i]->handler!=handler );
783         if( i >= 0 ) BC_KeyboardHandler::listeners.remove_object_number(i);
784 }
785
786 int BC_KeyboardHandler::run_event(BC_WindowBase *wp)
787 {
788         int result = (win->*handler)(wp);
789         return result;
790 }
791
792 int BC_KeyboardHandler::run_listeners(BC_WindowBase *wp)
793 {
794         int result = 0;
795         BC_KeyboardHandlerLock set;
796         for( int i=0; !result && i<listeners.size(); ++i ) {
797                 BC_KeyboardHandler *listener = listeners[i];
798                 result = listener->run_event(wp);
799         }
800         return result;
801 }
802
803 void BC_KeyboardHandler::kill_grabs()
804 {
805         BC_KeyboardHandlerLock set;
806         for( int i=0; i<listeners.size(); ++i ) {
807                 BC_WindowBase *win = listeners[i]->win;
808                 if( win->get_window_type() != POPUP_WINDOW ) continue;
809                 ((BC_Popup *)win)->ungrab_keyboard();
810         }
811 }
812
813 void BC_ActiveBitmaps::reque(XEvent *event)
814 {
815         XShmCompletionEvent *shm_ev = (XShmCompletionEvent *)event;
816         ShmSeg shmseg = shm_ev->shmseg;
817         Drawable drawable = shm_ev->drawable;
818 //printf("BC_BitmapImage::reque %08lx\n",shmseg);
819         active_lock.lock("BC_BitmapImage::reque");
820         BC_BitmapImage *bfr = first;
821         while( bfr && bfr->get_shmseg() != shmseg ) bfr = bfr->next;
822         if( bfr && bfr->drawable == drawable )
823                 remove_pointer(bfr);
824         active_lock.unlock();
825         if( !bfr ) {
826 // sadly, X reports two drawable completions and creates false reporting, so no boobytrap
827 //              printf("BC_BitmapImage::reque missed shmseg %08x, drawable %08x\n",
828 //                       (int)shmseg, (int)drawable);
829                 return;
830         }
831         if( bfr->drawable != drawable ) return;
832         if( bfr->is_zombie() ) { --BC_Bitmap::zombies; delete bfr; return; }
833         bfr->bitmap->reque(bfr);
834 }
835
836 void BC_ActiveBitmaps::insert(BC_BitmapImage *bfr, Drawable pixmap)
837 {
838         active_lock.lock("BC_BitmapImage::insert");
839         bfr->drawable = pixmap;
840         append(bfr);
841         active_lock.unlock();
842 }
843
844 void BC_ActiveBitmaps::remove_buffers(BC_WindowBase *wdw)
845 {
846         active_lock.lock("BC_ActiveBitmaps::remove");
847         for( BC_BitmapImage *nxt=0, *bfr=first; bfr; bfr=nxt ) {
848                 nxt = bfr->next;
849                 if( bfr->is_zombie() ) { --BC_Bitmap::zombies; delete bfr; continue; }
850                 if( bfr->bitmap->parent_window == wdw ) remove_pointer(bfr);
851         }
852         active_lock.unlock();
853 }
854
855 BC_ActiveBitmaps::BC_ActiveBitmaps()
856 {
857 }
858
859 BC_ActiveBitmaps::~BC_ActiveBitmaps()
860 {
861 }
862
863
864
865 int BC_WindowBase::keysym_lookup(XEvent *event)
866 {
867         for( int i = 0; i < KEYPRESSLEN; ++i ) keys_return[i] = 0;
868         for( int i = 0; i < 4; ++i ) wkey_string[i] = 0;
869
870         if( event->xany.send_event && !event->xany.serial ) {
871                 keysym = (KeySym) event->xkey.keycode;
872                 keys_return[0] = keysym;
873                 return 0;
874         }
875         wkey_string_length = 0;
876
877         if( input_context ) {
878                 wchar_t wkey[4];
879                 wkey_string_length = XwcLookupString(input_context,
880                         (XKeyEvent*)event, wkey, 4, &keysym, 0);
881                 for( int i=0; i<wkey_string_length; ++i )
882                         wkey_string[i] = wkey[i];
883 //printf("keysym_lookup 1 %d %d %lx %x %x %x %x\n", wkey_string_length, keysym,
884 //  wkey_string[0], wkey_string[1], wkey_string[2], wkey_string[3]);
885
886                 Status stat;
887                 int ret = Xutf8LookupString(input_context, (XKeyEvent*)event,
888                                 keys_return, KEYPRESSLEN, &keysym, &stat);
889 //printf("keysym_lookup 2 %d %d %lx %x %x\n", ret, stat, keysym, keys_return[0], keys_return[1]);
890                 if( stat == XLookupBoth ) return ret;
891                 if( stat == XLookupKeySym ) return 0;
892         }
893         int ret = XLookupString((XKeyEvent*)event, keys_return, KEYPRESSLEN, &keysym, 0);
894         wkey_string_length = ret;
895         for( int i=0; i<ret; ++i ) wkey_string[i] = keys_return[i];
896         return ret;
897 }
898
899 pthread_t locking_task = (pthread_t)-1L;
900 int locking_event = -1;
901 int locking_message = -1;
902
903 int BC_WindowBase::dispatch_event()
904 {
905         Window tempwin;
906         int result;
907         XClientMessageEvent *ptr;
908         int cancel_resize, cancel_translation;
909         volatile static int debug = 0;
910         XEvent *event;
911
912         key_pressed = 0;
913
914 #ifndef SINGLE_THREAD
915 // If an event is waiting get it, otherwise
916 // wait for next event only if there are no compressed events.
917         if(get_event_count() ||
918                 (!motion_events && !resize_events && !translation_events))
919         {
920                 event = get_event();
921 // Lock out window deletions
922                 lock_window("BC_WindowBase::dispatch_event 1");
923 locking_event = event->type;
924 locking_task = pthread_self();
925 locking_message = event->xclient.message_type;
926         }
927         else
928 // Handle compressed events
929         {
930                 lock_window("BC_WindowBase::dispatch_event 2");
931                 if(resize_events)
932                         dispatch_resize_event(last_resize_w, last_resize_h);
933                 if(motion_events)
934                         dispatch_motion_event();
935                 if(translation_events)
936                         dispatch_translation_event();
937
938                 unlock_window();
939                 return 0;
940         }
941
942 #endif
943
944
945
946
947 if( debug && event->type != ClientMessage ) {
948  static const char *event_names[] = {
949   "Reply", "Error", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease", "MotionNotify",
950   "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut", "KeymapNotify", "Expose", "GraphicsExpose",
951   "NoExpose", "VisibilityNotify", "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify",
952   "MapRequest", "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
953   "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify", "SelectionClear",
954   "SelectionRequest", "SelectionNotify", "ColormapNotify", "ClientMessage", "MappingNotify",
955   "GenericEvent", "LASTEvent",
956  };
957  const int nevents = sizeof(event_names)/sizeof(event_names[0]);
958
959  printf("BC_WindowBase::dispatch_event %d %s %p %d (%s)\n", __LINE__,
960   title, event, event->type, event->type>=0 && event->type<nevents ?
961    event_names[event->type] : "Unknown");
962 }
963
964         if( active_grab ) {
965                 grab_lock->lock("BC_WindowBase::dispatch_event 3");
966                 unlock_window();
967                 result = 0;
968                 if( active_grab ) {
969                         active_grab->lock_window("BC_WindowBase::dispatch_event 3");
970                         result = active_grab->grab_event(event);
971                         active_grab->unlock_window();
972                 }
973                 grab_lock->unlock();
974                 if( result ) return result;
975                 lock_window("BC_WindowBase::dispatch_event 4");
976         }
977
978         switch(event->type) {
979         case ClientMessage:
980 // Clear the resize buffer
981                 if( resize_events )
982                         dispatch_resize_event(last_resize_w, last_resize_h);
983 // Clear the motion buffer since this can clear the window
984                 if( motion_events )
985                         dispatch_motion_event();
986
987                 ptr = (XClientMessageEvent*)event;
988                 if( ptr->message_type == ProtoXAtom &&
989                     (Atom)ptr->data.l[0] == DelWinXAtom ) {
990                         close_event();
991                 }
992                 else if( ptr->message_type == RepeaterXAtom ) {
993                         dispatch_repeat_event(ptr->data.l[0]);
994                 }
995                 else if( ptr->message_type == SetDoneXAtom ) {
996                         done = 1;
997                 }
998                 else {
999                         receive_custom_xatoms((xatom_event *)ptr);
1000                 }
1001                 break;
1002
1003         case FocusIn:
1004                 has_focus = 1;
1005                 dispatch_focus_in();
1006                 break;
1007
1008         case FocusOut:
1009                 has_focus = 0;
1010                 dispatch_focus_out();
1011                 break;
1012
1013 // Maximized
1014         case MapNotify:
1015                 break;
1016
1017 // Minimized
1018         case UnmapNotify:
1019                 break;
1020
1021         case ButtonPress:
1022                 if(motion_events)
1023                 {
1024                         dispatch_motion_event();
1025                 }
1026                 get_key_masks(event->xbutton.state);
1027                 cursor_x = event->xbutton.x;
1028                 cursor_y = event->xbutton.y;
1029                 button_number = event->xbutton.button;
1030
1031 //printf("BC_WindowBase::dispatch_event %d %d\n", __LINE__, button_number);
1032                 event_win = event->xany.window;
1033                 if( button_number < 6 ) {
1034                         if( button_number < 4 )
1035                                 button_down = 1;
1036                         button_pressed = event->xbutton.button;
1037                         button_time1 = button_time2;
1038                         button_time2 = button_time3;
1039                         button_time3 = event->xbutton.time;
1040                         drag_x = cursor_x;
1041                         drag_y = cursor_y;
1042                         drag_win = event_win;
1043                         drag_x1 = cursor_x - get_resources()->drag_radius;
1044                         drag_x2 = cursor_x + get_resources()->drag_radius;
1045                         drag_y1 = cursor_y - get_resources()->drag_radius;
1046                         drag_y2 = cursor_y + get_resources()->drag_radius;
1047                         if( button_number < 4 ) {
1048                                 if((long)(button_time3 - button_time1) < resources->double_click * 2) {
1049                                         triple_click = 1;
1050                                         button_time3 = button_time2 = button_time1 = 0;
1051                                 }
1052                                 if((long)(button_time3 - button_time2) < resources->double_click) {
1053                                         double_click = 1;
1054 //                                      button_time3 = button_time2 = button_time1 = 0;
1055                                 }
1056                                 else {
1057                                         triple_click = 0;
1058                                         double_click = 0;
1059                                 }
1060                         }
1061                         dispatch_button_press();
1062                 }
1063                 break;
1064
1065         case ButtonRelease:
1066                 if(motion_events)
1067                 {
1068                         dispatch_motion_event();
1069                 }
1070                 get_key_masks(event->xbutton.state);
1071                 button_number = event->xbutton.button;
1072                 event_win = event->xany.window;
1073                 if (button_number < 6)
1074                 {
1075                         if(button_number < 4)
1076                                 button_down = 0;
1077 //printf("BC_WindowBase::dispatch_event %d %d\n", __LINE__, button_number);
1078
1079                         dispatch_button_release();
1080                 }
1081                 break;
1082
1083         case Expose:
1084                 event_win = event->xany.window;
1085                 result = 0;
1086                 for( int i=0; !result && i<popups.size(); ++i ) {  // popups take focus
1087                         if( popups[i]->win == event_win )
1088                                 result = popups[i]->dispatch_expose_event();
1089                 }
1090                 if( !result )
1091                         result = dispatch_expose_event();
1092                 break;
1093
1094         case MotionNotify:
1095                 get_key_masks(event->xmotion.state);
1096 // Dispatch previous motion event if this is a subsequent motion from a different window
1097                 if(motion_events && last_motion_win != event->xany.window)
1098                 {
1099                         dispatch_motion_event();
1100                 }
1101
1102 // Buffer the current motion
1103                 motion_events = 1;
1104                 last_motion_state = event->xmotion.state;
1105                 last_motion_x = event->xmotion.x;
1106                 last_motion_y = event->xmotion.y;
1107                 last_motion_win = event->xany.window;
1108                 break;
1109
1110         case ConfigureNotify:
1111 // printf("BC_WindowBase::dispatch_event %d win=%p this->win=%p\n",
1112 // __LINE__,
1113 // event->xany.window,
1114 // win);
1115 // dump_windows();
1116                 XTranslateCoordinates(top_level->display,
1117                         top_level->win,
1118                         top_level->rootwin,
1119                         0,
1120                         0,
1121                         &last_translate_x,
1122                         &last_translate_y,
1123                         &tempwin);
1124                 last_resize_w = event->xconfigure.width;
1125                 last_resize_h = event->xconfigure.height;
1126
1127                 cancel_resize = 0;
1128                 cancel_translation = 0;
1129
1130 // Resize history prevents responses to recursive resize requests
1131                 for(int i = 0; i < resize_history.total && !cancel_resize; i++)
1132                 {
1133                         if(resize_history.values[i]->w == last_resize_w &&
1134                                 resize_history.values[i]->h == last_resize_h)
1135                         {
1136                                 delete resize_history.values[i];
1137                                 resize_history.remove_number(i);
1138                                 cancel_resize = 1;
1139                         }
1140                 }
1141
1142                 if(last_resize_w == w && last_resize_h == h)
1143                         cancel_resize = 1;
1144
1145                 if(!cancel_resize)
1146                 {
1147                         resize_events = 1;
1148                 }
1149
1150                 if((last_translate_x == x && last_translate_y == y))
1151                         cancel_translation = 1;
1152
1153                 if(!cancel_translation)
1154                 {
1155                         translation_events = 1;
1156                 }
1157
1158                 translation_count++;
1159                 break;
1160
1161         case KeyPress:
1162                 get_key_masks(event->xkey.state);
1163                 keys_return[0] = 0;  keysym = -1;
1164                 if(XFilterEvent(event, win)) {
1165                         break;
1166                 }
1167                 if( keysym_lookup(event) < 0 ) {
1168                         printf("keysym %x\n", (uint32_t)keysym);
1169                         break;
1170                 }
1171
1172 //printf("BC_WindowBase::dispatch_event %d keysym=0x%x\n",
1173 //__LINE__,
1174 //keysym);
1175
1176 // block out control keys
1177                 if(keysym > 0xffe0 && keysym < 0xffff) break;
1178 // block out Alt_GR key
1179                 if(keysym == 0xfe03) break;
1180
1181                 if(test_keypress)
1182                          printf("BC_WindowBase::dispatch_event %x\n", (uint32_t)keysym);
1183
1184 #ifdef X_HAVE_UTF8_STRING
1185 //It's Ascii or UTF8?
1186 //              if (keysym != 0xffff && (keys_return[0] & 0xff) >= 0x7f )
1187 //printf("BC_WindowBase::dispatch_event %d %02x%02x\n", __LINE__, keys_return[0], keys_return[1]);
1188
1189                 if( ((keys_return[1] & 0xff) > 0x80) &&
1190                     ((keys_return[0] & 0xff) > 0xC0) ) {
1191 //printf("BC_WindowBase::dispatch_event %d\n", __LINE__);
1192                         key_pressed = keysym & 0xff;
1193                 }
1194                 else {
1195 #endif
1196 // shuttle speed codes
1197                 if( keysym >= SKEY_MIN && keysym <= SKEY_MAX ) {
1198                         key_pressed = keysym;
1199                 }
1200                 else switch( keysym ) {
1201 // block out extra keys
1202                 case XK_Alt_L:
1203                 case XK_Alt_R:
1204                 case XK_Shift_L:
1205                 case XK_Shift_R:
1206                 case XK_Control_L:
1207                 case XK_Control_R:
1208                         key_pressed = 0;
1209                         break;
1210
1211 // Translate key codes
1212                 case XK_Return:         key_pressed = RETURN;   break;
1213                 case XK_Up:             key_pressed = UP;       break;
1214                 case XK_Down:           key_pressed = DOWN;     break;
1215                 case XK_Left:           key_pressed = LEFT;     break;
1216                 case XK_Right:          key_pressed = RIGHT;    break;
1217                 case XK_Next:           key_pressed = PGDN;     break;
1218                 case XK_Prior:          key_pressed = PGUP;     break;
1219                 case XK_BackSpace:      key_pressed = BACKSPACE; break;
1220                 case XK_Escape:         key_pressed = ESC;      break;
1221                 case XK_Tab:
1222                         if(shift_down())
1223                                 key_pressed = LEFTTAB;
1224                         else
1225                                 key_pressed = TAB;
1226                         break;
1227                 case XK_ISO_Left_Tab:   key_pressed = LEFTTAB;  break;
1228                 case XK_underscore:     key_pressed = '_';      break;
1229                 case XK_asciitilde:     key_pressed = '~';      break;
1230                 case XK_Delete:         key_pressed = DELETE;   break;
1231                 case XK_Home:           key_pressed = HOME;     break;
1232                 case XK_End:            key_pressed = END;      break;
1233
1234 // number pad
1235                 case XK_KP_Enter:       key_pressed = KPENTER;  break;
1236                 case XK_KP_Add:         key_pressed = KPPLUS;   break;
1237                 case XK_KP_Subtract:    key_pressed = KPMINUS;  break;
1238                 case XK_KP_Multiply:    key_pressed = KPSTAR;   break;
1239                 case XK_KP_Divide:      key_pressed = KPSLASH;  break;
1240                 case XK_KP_1:
1241                 case XK_KP_End:         key_pressed = KP1;      break;
1242                 case XK_KP_2:
1243                 case XK_KP_Down:        key_pressed = KP2;      break;
1244                 case XK_KP_3:
1245                 case XK_KP_Page_Down:   key_pressed = KP3;      break;
1246                 case XK_KP_4:
1247                 case XK_KP_Left:        key_pressed = KP4;      break;
1248                 case XK_KP_5:
1249                 case XK_KP_Begin:       key_pressed = KP5;      break;
1250                 case XK_KP_6:
1251                 case XK_KP_Right:       key_pressed = KP6;      break;
1252                 case XK_KP_7:
1253                 case XK_KP_Home:        key_pressed = KP7;      break;
1254                 case XK_KP_8:
1255                 case XK_KP_Up:          key_pressed = KP8;      break;
1256                 case XK_KP_9:
1257                 case XK_KP_Page_Up:     key_pressed = KP9;      break;
1258                 case XK_KP_0:
1259                 case XK_KP_Insert:      key_pressed = KPINS;    break;
1260                 case XK_KP_Decimal:
1261                 case XK_KP_Delete:      key_pressed = KPDEL;    break;
1262
1263                 case XK_F1:             key_pressed = KEY_F1;   break;
1264                 case XK_F2:             key_pressed = KEY_F2;   break;
1265                 case XK_F3:             key_pressed = KEY_F3;   break;
1266                 case XK_F4:             key_pressed = KEY_F4;   break;
1267                 case XK_F5:             key_pressed = KEY_F5;   break;
1268                 case XK_F6:             key_pressed = KEY_F6;   break;
1269                 case XK_F7:             key_pressed = KEY_F7;   break;
1270                 case XK_F8:             key_pressed = KEY_F8;   break;
1271                 case XK_F9:             key_pressed = KEY_F9;   break;
1272                 case XK_F10:            key_pressed = KEY_F10;  break;
1273                 case XK_F11:            key_pressed = KEY_F11;  break;
1274                 case XK_F12:            key_pressed = KEY_F12;  break;
1275 // activates remote
1276                 case XK_Menu:           key_pressed = KPMENU;   break;  /* menu */
1277
1278                 default:
1279                         key_pressed = keysym & 0xff;
1280 #ifdef X_HAVE_UTF8_STRING
1281 //printf("BC_WindowBase::dispatch_event %d\n", __LINE__);
1282                         keys_return[1] = 0;
1283 #endif
1284                         break;
1285                 }
1286 #ifdef X_HAVE_UTF8_STRING
1287                 }
1288                 key_pressed_utf8 = keys_return;
1289 #endif
1290
1291
1292                 result = 0;
1293                 if( top_level == this )
1294                         result = BC_KeyboardHandler::run_listeners(this);
1295
1296 //printf("BC_WindowBase::dispatch_event %d %d %x\n", shift_down(), alt_down(), key_pressed);
1297                 if( !result )
1298                         result = dispatch_keypress_event();
1299 // Handle some default keypresses
1300                 if(!result)
1301                 {
1302                         if(key_pressed == 'w' ||
1303                                 key_pressed == 'W')
1304                         {
1305                                 close_event();
1306                         }
1307                 }
1308                 break;
1309
1310         case KeyRelease:
1311                 XLookupString((XKeyEvent*)event, keys_return, 1, &keysym, 0);
1312                 dispatch_keyrelease_event();
1313 // printf("BC_WindowBase::dispatch_event KeyRelease keysym=0x%x keystate=0x%lld\n",
1314 // keysym, event->xkey.state);
1315                 break;
1316
1317         case LeaveNotify:
1318                 if( event->xcrossing.mode != NotifyNormal ) break;
1319                 cursor_entered = 0;
1320                 event_win = event->xany.window;
1321                 dispatch_cursor_leave();
1322                 break;
1323
1324         case EnterNotify:
1325                 if( event->xcrossing.mode != NotifyNormal ) break;
1326
1327                 if( !cursor_entered ) {
1328                         for( int i=0; i<popups.size(); ++i ) {  // popups always take focus
1329                                 if( popups[i]->win == event->xcrossing.window )
1330                                 cursor_entered = 1;
1331                         }
1332                         if( !cursor_entered && get_resources()->grab_input_focus &&
1333                             !event->xcrossing.focus && event->xcrossing.window == win ) {
1334                                 cursor_entered = 1;
1335                         }
1336                         if( cursor_entered )
1337                                 focus();
1338                 }
1339                 event_win = event->xany.window;
1340                 cursor_x = event->xcrossing.x;
1341                 cursor_y = event->xcrossing.y;
1342                 dispatch_cursor_enter();
1343                 break;
1344
1345         default:
1346                 break;
1347         }
1348 //printf("100 %s %p %d\n", title, event, event->type);
1349 //if(event->type != ClientMessage) dump();
1350
1351 #ifndef SINGLE_THREAD
1352         unlock_window();
1353         if(event) {
1354                 if( resend_event_window ) {
1355                         resend_event_window->put_event(event);
1356                         resend_event_window = 0;
1357                 }
1358                 else
1359                         delete event;
1360         }
1361 #else
1362 //      if(done) completion_lock->unlock();
1363 #endif
1364
1365 if(debug) printf("BC_WindowBase::dispatch_event this=%p %d\n", this, __LINE__);
1366         return 0;
1367 }
1368
1369 int BC_WindowBase::dispatch_expose_event()
1370 {
1371         int result = 0;
1372         for(int i = 0; i < subwindows->total && !result; i++)
1373         {
1374                 result = subwindows->values[i]->dispatch_expose_event();
1375         }
1376
1377 // Propagate to user
1378         if(!result) expose_event();
1379         return result;
1380 }
1381
1382 int BC_WindowBase::dispatch_resize_event(int w, int h)
1383 {
1384 // Can't store new w and h until the event is handles
1385 // because bcfilebox depends on the old w and h to
1386 // reposition widgets.
1387         if( window_type == MAIN_WINDOW ) {
1388                 flash_enabled = 0;
1389                 resize_events = 0;
1390
1391                 delete pixmap;
1392                 pixmap = new BC_Pixmap(this, w, h);
1393                 clear_box(0, 0, w, h);
1394         }
1395
1396 // Propagate to subwindows
1397         for(int i = 0; i < subwindows->total; i++) {
1398                 subwindows->values[i]->dispatch_resize_event(w, h);
1399         }
1400
1401 // Propagate to user
1402         resize_event(w, h);
1403
1404         if( window_type == MAIN_WINDOW ) {
1405                 this->w = w;
1406                 this->h = h;
1407                 dispatch_flash();
1408                 flush();
1409         }
1410         return 0;
1411 }
1412
1413 int BC_WindowBase::dispatch_flash()
1414 {
1415         flash_enabled = 1;
1416         for(int i = 0; i < subwindows->total; i++)
1417                 subwindows->values[i]->dispatch_flash();
1418         return flash(0);
1419 }
1420
1421 int BC_WindowBase::dispatch_translation_event()
1422 {
1423         translation_events = 0;
1424         if(window_type == MAIN_WINDOW)
1425         {
1426                 prev_x = x;
1427                 prev_y = y;
1428                 x = last_translate_x;
1429                 y = last_translate_y;
1430 // Correct for window manager offsets
1431                 x -= x_correction;
1432                 y -= y_correction;
1433         }
1434
1435         for(int i = 0; i < subwindows->total; i++)
1436         {
1437                 subwindows->values[i]->dispatch_translation_event();
1438         }
1439
1440         translation_event();
1441         return 0;
1442 }
1443
1444 int BC_WindowBase::dispatch_motion_event()
1445 {
1446         int result = 0;
1447         unhide_cursor();
1448
1449         if(top_level == this)
1450         {
1451                 motion_events = 0;
1452                 event_win = last_motion_win;
1453                 get_key_masks(last_motion_state);
1454
1455 // Test for grab
1456                 if(get_button_down() && !active_menubar && !active_popup_menu)
1457                 {
1458                         if(!result)
1459                         {
1460                                 cursor_x = last_motion_x;
1461                                 cursor_y = last_motion_y;
1462                                 result = dispatch_drag_motion();
1463                         }
1464
1465                         if(!result &&
1466                                 (last_motion_x < drag_x1 || last_motion_x >= drag_x2 ||
1467                                 last_motion_y < drag_y1 || last_motion_y >= drag_y2))
1468                         {
1469                                 cursor_x = drag_x;
1470                                 cursor_y = drag_y;
1471
1472                                 result = dispatch_drag_start();
1473                         }
1474                 }
1475
1476                 cursor_x = last_motion_x;
1477                 cursor_y = last_motion_y;
1478
1479 // printf("BC_WindowBase::dispatch_motion_event %d %p %p %p\n",
1480 // __LINE__,
1481 // active_menubar,
1482 // active_popup_menu,
1483 // active_subwindow);
1484
1485                 if(active_menubar && !result) result = active_menubar->dispatch_motion_event();
1486                 if(active_popup_menu && !result) result = active_popup_menu->dispatch_motion_event();
1487                 if(active_subwindow && !result) result = active_subwindow->dispatch_motion_event();
1488         }
1489
1490 // Dispatch in stacking order
1491         for(int i = subwindows->size() - 1; i >= 0 && !result; i--)
1492         {
1493                 result = subwindows->values[i]->dispatch_motion_event();
1494         }
1495
1496         if(!result) result = cursor_motion_event();    // give to user
1497         return result;
1498 }
1499
1500 int BC_WindowBase::dispatch_keypress_event()
1501 {
1502         int result = 0;
1503         if(top_level == this)
1504         {
1505                 if(active_subwindow) result = active_subwindow->dispatch_keypress_event();
1506         }
1507
1508         for(int i = 0; i < subwindows->total && !result; i++)
1509         {
1510                 result = subwindows->values[i]->dispatch_keypress_event();
1511         }
1512
1513         if(!result) result = keypress_event();
1514
1515         return result;
1516 }
1517
1518 int BC_WindowBase::dispatch_keyrelease_event()
1519 {
1520         int result = 0;
1521         if(top_level == this)
1522         {
1523                 if(active_subwindow) result = active_subwindow->dispatch_keyrelease_event();
1524         }
1525
1526         for(int i = 0; i < subwindows->total && !result; i++)
1527         {
1528                 result = subwindows->values[i]->dispatch_keyrelease_event();
1529         }
1530
1531         if(!result) result = keyrelease_event();
1532
1533         return result;
1534 }
1535
1536 int BC_WindowBase::dispatch_focus_in()
1537 {
1538         for(int i = 0; i < subwindows->total; i++)
1539         {
1540                 subwindows->values[i]->dispatch_focus_in();
1541         }
1542
1543         focus_in_event();
1544
1545         return 0;
1546 }
1547
1548 int BC_WindowBase::dispatch_focus_out()
1549 {
1550         for(int i = 0; i < subwindows->total; i++)
1551         {
1552                 subwindows->values[i]->dispatch_focus_out();
1553         }
1554
1555         focus_out_event();
1556
1557         return 0;
1558 }
1559
1560 int BC_WindowBase::get_has_focus()
1561 {
1562         return top_level->has_focus;
1563 }
1564
1565 int BC_WindowBase::get_deleting()
1566 {
1567         if(is_deleting) return 1;
1568         if(parent_window && parent_window->get_deleting()) return 1;
1569         return 0;
1570 }
1571
1572 int BC_WindowBase::dispatch_button_press()
1573 {
1574         int result = 0;
1575
1576
1577         if(top_level == this)
1578         {
1579                 if(active_menubar) result = active_menubar->dispatch_button_press();
1580                 if(active_popup_menu && !result) result = active_popup_menu->dispatch_button_press();
1581                 if(active_subwindow && !result) result = active_subwindow->dispatch_button_press();
1582         }
1583
1584         for(int i = 0; i < subwindows->total && !result; i++)
1585         {
1586                 result = subwindows->values[i]->dispatch_button_press();
1587         }
1588
1589         if(!result) result = button_press_event();
1590
1591
1592         return result;
1593 }
1594
1595 int BC_WindowBase::dispatch_button_release()
1596 {
1597         int result = 0;
1598         if(top_level == this)
1599         {
1600                 if(active_menubar) result = active_menubar->dispatch_button_release();
1601                 if(active_popup_menu && !result) result = active_popup_menu->dispatch_button_release();
1602                 if(active_subwindow && !result) result = active_subwindow->dispatch_button_release();
1603                 if(!result && button_number != 4 && button_number != 5)
1604                         result = dispatch_drag_stop();
1605         }
1606
1607         for(int i = 0; i < subwindows->total && !result; i++)
1608         {
1609                 result = subwindows->values[i]->dispatch_button_release();
1610         }
1611
1612         if(!result)
1613         {
1614                 result = button_release_event();
1615         }
1616
1617         return result;
1618 }
1619
1620
1621 int BC_WindowBase::dispatch_repeat_event(int64_t duration)
1622 {
1623
1624 // all repeat event handlers get called and decide based on activity and duration
1625 // whether to respond
1626         for(int i = 0; i < subwindows->total; i++)
1627         {
1628                 subwindows->values[i]->dispatch_repeat_event(duration);
1629         }
1630
1631
1632         repeat_event(duration);
1633
1634
1635
1636 // Unlock next repeat signal
1637         if(window_type == MAIN_WINDOW)
1638         {
1639 #ifdef SINGLE_THREAD
1640                 BC_Display::display_global->unlock_repeaters(duration);
1641 #else
1642                 for(int i = 0; i < repeaters.total; i++)
1643                 {
1644                         if(repeaters.values[i]->delay == duration)
1645                         {
1646                                 repeaters.values[i]->repeat_lock->unlock();
1647                         }
1648                 }
1649 #endif
1650         }
1651         return 0;
1652 }
1653
1654 void BC_WindowBase::unhide_cursor()
1655 {
1656         if(is_transparent)
1657         {
1658                 is_transparent = 0;
1659                 if(top_level->is_hourglass)
1660                         set_cursor(HOURGLASS_CURSOR, 1, 0);
1661                 else
1662                         set_cursor(current_cursor, 1, 0);
1663         }
1664         cursor_timer->update();
1665 }
1666
1667
1668 void BC_WindowBase::update_video_cursor()
1669 {
1670         if(video_on && !is_transparent)
1671         {
1672                 if(cursor_timer->get_difference() > VIDEO_CURSOR_TIMEOUT && !is_transparent)
1673                 {
1674                         is_transparent = 1;
1675                         set_cursor(TRANSPARENT_CURSOR, 1, 1);
1676                         cursor_timer->update();
1677                 }
1678         }
1679         else
1680         {
1681                 cursor_timer->update();
1682         }
1683 }
1684
1685
1686 int BC_WindowBase::dispatch_cursor_leave()
1687 {
1688         unhide_cursor();
1689
1690         for(int i = 0; i < subwindows->total; i++)
1691         {
1692                 subwindows->values[i]->dispatch_cursor_leave();
1693         }
1694
1695         cursor_leave_event();
1696         return 0;
1697 }
1698
1699 int BC_WindowBase::dispatch_cursor_enter()
1700 {
1701         int result = 0;
1702
1703         unhide_cursor();
1704
1705         if(active_menubar) result = active_menubar->dispatch_cursor_enter();
1706         if(!result && active_popup_menu) result = active_popup_menu->dispatch_cursor_enter();
1707         if(!result && active_subwindow) result = active_subwindow->dispatch_cursor_enter();
1708
1709         for(int i = 0; !result && i < subwindows->total; i++)
1710         {
1711                 result = subwindows->values[i]->dispatch_cursor_enter();
1712         }
1713
1714         if(!result) result = cursor_enter_event();
1715         return result;
1716 }
1717
1718 int BC_WindowBase::cursor_enter_event()
1719 {
1720         return 0;
1721 }
1722
1723 int BC_WindowBase::cursor_leave_event()
1724 {
1725         return 0;
1726 }
1727
1728 int BC_WindowBase::close_event()
1729 {
1730         set_done(1);
1731         return 1;
1732 }
1733
1734 // *** CONTEXT_HELP ***
1735 // This basic implementation serves solely for context help.
1736 // We are handling Alt/H only. Any subclass requiring more sophisticated
1737 // processing of keystrokes has to provide its own overloaded handler.
1738 int BC_WindowBase::keypress_event()
1739 {
1740 //      printf("BC_WindowBase::keypress_event: %d\n", get_keypress());
1741         return context_help_check_and_show();
1742 }
1743
1744 // The stuff up to END_CONTEXT_HELP serves solely for context help
1745 void BC_WindowBase::context_help_set_keyword(const char *keyword)
1746 {
1747         if (keyword) strcpy(context_help_keyword, keyword);
1748 }
1749
1750 const char *BC_WindowBase::context_help_get_keyword()
1751 {
1752 // If we have context_help_keyword defined here, return it.
1753 // Otherwise recursively track widget hierarchy up to top_level
1754 // and return the nearest found nonempty keyword.
1755 // If nothing found, the special keyword "TOC" is returned.
1756         if (context_help_keyword[0] || this == top_level ||
1757             this == parent_window || ! parent_window) {
1758                 if (! context_help_keyword[0] && this == top_level)
1759                         context_help_set_keyword("TOC");
1760                 return context_help_keyword;
1761         }
1762         return parent_window->context_help_get_keyword();
1763 }
1764
1765 void BC_WindowBase::context_help_show(const char *keyword)
1766 {
1767         char cmd[BCTEXTLEN];
1768
1769         if (! keyword) return;
1770
1771         sprintf(cmd, "\"%s/doc/ContextManual.pl\" \"%s\"", getenv("CIN_DAT"),
1772                 keyword);
1773 //      printf("BC_WindowBase::context_help_show(%s):\n%s\n", keyword, cmd);
1774
1775 // ContextManual.pl starts browser in background, so system() should not block
1776         system(cmd);
1777 }
1778
1779 void BC_WindowBase::context_help_show()
1780 {
1781         context_help_show(context_help_get_keyword());
1782 }
1783
1784 int BC_WindowBase::context_help_check_and_show(const char *keyword)
1785 {
1786         if (! keyword)    return 0;
1787         if (! keyword[0]) return 0;
1788
1789 // We are handling Alt/H only
1790         if (get_keypress() != 'h' || ! alt_down()) return 0;
1791
1792 // Restrict cursor location to that widget keystroke occured in
1793         if (! is_tooltip_event_win() || ! cursor_inside()) return 0;
1794
1795         context_help_show(keyword);
1796         return 1;
1797 }
1798
1799 int BC_WindowBase::context_help_check_and_show()
1800 {
1801         const char *keyword;
1802
1803 // We are handling Alt/H only
1804         if (get_keypress() != 'h' || ! alt_down()) return 0;
1805
1806 // Restrict cursor location, so any subwindow can define its own help keyword
1807         if (! is_tooltip_event_win() || ! cursor_inside()) return 0;
1808
1809 // If a widget has not defined its own help keyword, the parent can provide it
1810         keyword = context_help_get_keyword();
1811         if (! keyword[0]) return 0;
1812
1813         context_help_show(keyword);
1814         return 1;
1815 }
1816 // *** END_CONTEXT_HELP ***
1817
1818 int BC_WindowBase::dispatch_drag_start()
1819 {
1820         int result = 0;
1821         if(active_menubar) result = active_menubar->dispatch_drag_start();
1822         if(!result && active_popup_menu) result = active_popup_menu->dispatch_drag_start();
1823         if(!result && active_subwindow) result = active_subwindow->dispatch_drag_start();
1824
1825         for(int i = 0; i < subwindows->total && !result; i++)
1826         {
1827                 result = subwindows->values[i]->dispatch_drag_start();
1828         }
1829
1830         if(!result) result = is_dragging = drag_start_event();
1831         return result;
1832 }
1833
1834 int BC_WindowBase::dispatch_drag_stop()
1835 {
1836         int result = 0;
1837
1838         for(int i = 0; i < subwindows->total && !result; i++)
1839         {
1840                 result = subwindows->values[i]->dispatch_drag_stop();
1841         }
1842
1843         if(is_dragging && !result)
1844         {
1845                 drag_stop_event();
1846                 is_dragging = 0;
1847                 result = 1;
1848         }
1849
1850         return result;
1851 }
1852
1853 int BC_WindowBase::dispatch_drag_motion()
1854 {
1855         int result = 0;
1856         for(int i = 0; i < subwindows->total && !result; i++)
1857         {
1858                 result = subwindows->values[i]->dispatch_drag_motion();
1859         }
1860
1861         if(is_dragging && !result)
1862         {
1863                 drag_motion_event();
1864                 result = 1;
1865         }
1866
1867         return result;
1868 }
1869
1870
1871 int BC_WindowBase::show_tooltip(const char *text, int x, int y, int w, int h)
1872 {
1873 // default text
1874         int forced = !text ? force_tooltip : 1;
1875         if( !text ) text = tooltip_text;
1876         if( !text || (!forced && !get_resources()->tooltips_enabled) ) {
1877                 top_level->hide_tooltip();
1878                 return 1;
1879         }
1880 // default w,h
1881         if(w < 0) w = get_text_width(MEDIUMFONT, text)  + TOOLTIP_MARGIN * 2;
1882         if(h < 0) h = get_text_height(MEDIUMFONT, text) + TOOLTIP_MARGIN * 2;
1883 // default x,y (win relative)
1884         if( x < 0 ) x = get_w();
1885         if( y < 0 ) y = get_h();
1886         int wx, wy;
1887         get_root_coordinates(x, y, &wx, &wy);
1888 // keep the tip inside the window/display
1889         int x0 = top_level->get_x(), x1 = x0 + top_level->get_w();
1890         int x2 = top_level->get_screen_x(0, -1) + top_level->get_screen_w(0, -1);
1891         if( x1 > x2 ) x1 = x2;
1892         if( wx < x0 ) wx = x0;
1893         if( wx >= (x1-=w) ) wx = x1;
1894         int y0 = top_level->get_y(), y1 = y0 + top_level->get_h();
1895         int y2 = top_level->get_root_h(0);
1896         if( y1 > y2 ) y1 = y2;
1897         if( wy < y0 ) wy = y0;
1898         if( wy >= (y1-=h) ) wy = y1;
1899 // avoid tip under cursor (flickers)
1900         int abs_x, abs_y;
1901         get_abs_cursor(abs_x,abs_y, 0);
1902         if( wx < abs_x && abs_x < wx+w && wy < abs_y && abs_y < wy+h ) {
1903                 if( wx-abs_x < wy-abs_y )
1904                         wx = abs_x+1;
1905                 else
1906                         wy = abs_y+1;
1907         }
1908         if( !tooltip_on ) {
1909                 tooltip_on = 1;
1910                 tooltip_popup = new BC_Popup(top_level, wx, wy, w, h,
1911                                 get_resources()->tooltip_bg_color);
1912         }
1913         else
1914                 tooltip_popup->reposition_window(wx, wy, w, h);
1915
1916         draw_tooltip(text);
1917         tooltip_popup->flash();
1918         tooltip_popup->flush();
1919         return 0;
1920 }
1921
1922 int BC_WindowBase::hide_tooltip()
1923 {
1924         if(subwindows)
1925                 for(int i = 0; i < subwindows->total; i++)
1926                 {
1927                         subwindows->values[i]->hide_tooltip();
1928                 }
1929
1930         if(tooltip_on)
1931         {
1932                 tooltip_on = 0;
1933                 delete tooltip_popup;
1934                 tooltip_popup = 0;
1935         }
1936         return 0;
1937 }
1938
1939 const char *BC_WindowBase::get_tooltip()
1940 {
1941         return tooltip_text;
1942 }
1943
1944 int BC_WindowBase::set_tooltip(const char *text)
1945 {
1946         tooltip_text = text;
1947
1948 // Update existing tooltip if it is visible
1949         if(tooltip_on)
1950         {
1951                 draw_tooltip();
1952                 tooltip_popup->flash();
1953         }
1954         return 0;
1955 }
1956 // signal the event handler to repeat
1957 int BC_WindowBase::set_repeat(int64_t duration)
1958 {
1959         if(duration <= 0)
1960         {
1961                 printf("BC_WindowBase::set_repeat duration=%jd\n", duration);
1962                 return 0;
1963         }
1964         if(window_type != MAIN_WINDOW) return top_level->set_repeat(duration);
1965
1966 #ifdef SINGLE_THREAD
1967         BC_Display::display_global->set_repeat(this, duration);
1968 #else
1969 // test repeater database for duplicates
1970         for(int i = 0; i < repeaters.total; i++)
1971         {
1972 // Already exists
1973                 if(repeaters.values[i]->delay == duration)
1974                 {
1975                         repeaters.values[i]->start_repeating(this);
1976                         return 0;
1977                 }
1978         }
1979
1980         BC_Repeater *repeater = new BC_Repeater(this, duration);
1981         repeater->initialize();
1982         repeaters.append(repeater);
1983         repeater->start_repeating();
1984 #endif
1985         return 0;
1986 }
1987
1988 int BC_WindowBase::unset_repeat(int64_t duration)
1989 {
1990         if(window_type != MAIN_WINDOW) return top_level->unset_repeat(duration);
1991
1992 #ifdef SINGLE_THREAD
1993         BC_Display::display_global->unset_repeat(this, duration);
1994 #else
1995         for(int i = 0; i < repeaters.total; i++)
1996         {
1997                 if(repeaters.values[i]->delay == duration)
1998                 {
1999                         repeaters.values[i]->stop_repeating();
2000                 }
2001         }
2002 #endif
2003         return 0;
2004 }
2005
2006
2007 int BC_WindowBase::unset_all_repeaters()
2008 {
2009 #ifdef SINGLE_THREAD
2010         BC_Display::display_global->unset_all_repeaters(this);
2011 #else
2012         for(int i = 0; i < repeaters.total; i++)
2013         {
2014                 repeaters.values[i]->stop_repeating();
2015         }
2016         repeaters.remove_all_objects();
2017 #endif
2018         return 0;
2019 }
2020
2021 // long BC_WindowBase::get_repeat_id()
2022 // {
2023 //      return top_level->next_repeat_id++;
2024 // }
2025
2026 XEvent *BC_WindowBase::new_xevent()
2027 {
2028         XEvent *event = new XEvent;
2029         memset(event, 0, sizeof(*event));
2030         return event;
2031 }
2032
2033 #ifndef SINGLE_THREAD
2034 int BC_WindowBase::arm_repeat(int64_t duration)
2035 {
2036         XEvent *event = new_xevent();
2037         XClientMessageEvent *ptr = (XClientMessageEvent*)event;
2038         ptr->type = ClientMessage;
2039         ptr->message_type = RepeaterXAtom;
2040         ptr->format = 32;
2041         ptr->data.l[0] = duration;
2042
2043 // Couldn't use XSendEvent since it locked up randomly.
2044         put_event(event);
2045         return 0;
2046 }
2047 #endif
2048
2049 int BC_WindowBase::receive_custom_xatoms(xatom_event *event)
2050 {
2051         return 0;
2052 }
2053
2054 int BC_WindowBase::send_custom_xatom(xatom_event *event)
2055 {
2056 #ifndef SINGLE_THREAD
2057         XEvent *myevent = new_xevent();
2058         XClientMessageEvent *ptr = (XClientMessageEvent*)myevent;
2059         ptr->type = ClientMessage;
2060         ptr->message_type = event->message_type;
2061         ptr->format = event->format;
2062         ptr->data.l[0] = event->data.l[0];
2063         ptr->data.l[1] = event->data.l[1];
2064         ptr->data.l[2] = event->data.l[2];
2065         ptr->data.l[3] = event->data.l[3];
2066         ptr->data.l[4] = event->data.l[4];
2067
2068         put_event(myevent);
2069 #endif
2070         return 0;
2071 }
2072
2073
2074
2075 Atom BC_WindowBase::create_xatom(const char *atom_name)
2076 {
2077         return XInternAtom(display, atom_name, False);
2078 }
2079
2080 int BC_WindowBase::get_atoms()
2081 {
2082         SetDoneXAtom =  XInternAtom(display, "BC_REPEAT_EVENT", False);
2083         RepeaterXAtom = XInternAtom(display, "BC_CLOSE_EVENT", False);
2084         DestroyAtom =   XInternAtom(display, "BC_DESTROY_WINDOW", False);
2085         DelWinXAtom =   XInternAtom(display, "WM_DELETE_WINDOW", False);
2086         if( (ProtoXAtom = XInternAtom(display, "WM_PROTOCOLS", False)) != 0 )
2087                 XChangeProperty(display, win, ProtoXAtom, XA_ATOM, 32,
2088                         PropModeReplace, (unsigned char *)&DelWinXAtom, True);
2089         return 0;
2090
2091 }
2092
2093
2094 void BC_WindowBase::init_cursors()
2095 {
2096         arrow_cursor = XCreateFontCursor(display, XC_top_left_arrow);
2097         cross_cursor = XCreateFontCursor(display, XC_crosshair);
2098         ibeam_cursor = XCreateFontCursor(display, XC_xterm);
2099         vseparate_cursor = XCreateFontCursor(display, XC_sb_v_double_arrow);
2100         hseparate_cursor = XCreateFontCursor(display, XC_sb_h_double_arrow);
2101         move_cursor = XCreateFontCursor(display, XC_fleur);
2102         left_cursor = XCreateFontCursor(display, XC_sb_left_arrow);
2103         right_cursor = XCreateFontCursor(display, XC_sb_right_arrow);
2104         upright_arrow_cursor = XCreateFontCursor(display, XC_arrow);
2105         upleft_resize_cursor = XCreateFontCursor(display, XC_top_left_corner);
2106         upright_resize_cursor = XCreateFontCursor(display, XC_top_right_corner);
2107         downleft_resize_cursor = XCreateFontCursor(display, XC_bottom_left_corner);
2108         downright_resize_cursor = XCreateFontCursor(display, XC_bottom_right_corner);
2109         hourglass_cursor = XCreateFontCursor(display, XC_watch);
2110         grabbed_cursor = create_grab_cursor();
2111
2112         static char cursor_data[] = { 0,0,0,0, 0,0,0,0 };
2113         Colormap colormap = DefaultColormap(display, screen);
2114         Pixmap pixmap_bottom = XCreateBitmapFromData(display,
2115                 rootwin, cursor_data, 8, 8);
2116         XColor black, dummy;
2117         XAllocNamedColor(display, colormap, "black", &black, &dummy);
2118         transparent_cursor = XCreatePixmapCursor(display,
2119                 pixmap_bottom, pixmap_bottom, &black, &black, 0, 0);
2120 //      XDefineCursor(display, win, transparent_cursor);
2121         XFreePixmap(display, pixmap_bottom);
2122 }
2123
2124 int BC_WindowBase::evaluate_color_model(int client_byte_order, int server_byte_order, int depth)
2125 {
2126         int color_model = BC_TRANSPARENCY;
2127         switch(depth)
2128         {
2129                 case 8:
2130                         color_model = BC_RGB8;
2131                         break;
2132                 case 16:
2133                         color_model = (server_byte_order == client_byte_order) ? BC_RGB565 : BC_BGR565;
2134                         break;
2135                 case 24:
2136                         color_model = server_byte_order ? BC_BGR888 : BC_RGB888;
2137                         break;
2138                 case 32:
2139                         color_model = server_byte_order ? BC_BGR8888 : BC_ARGB8888;
2140                         break;
2141         }
2142         return color_model;
2143 }
2144
2145 int BC_WindowBase::init_colors()
2146 {
2147         total_colors = 0;
2148         current_color_value = current_color_pixel = 0;
2149
2150 // Get the real depth
2151         char *data = 0;
2152         XImage *ximage;
2153         ximage = XCreateImage(top_level->display,
2154                         top_level->vis, top_level->default_depth,
2155                         ZPixmap, 0, data, 16, 16, 8, 0);
2156         bits_per_pixel = ximage->bits_per_pixel;
2157         XDestroyImage(ximage);
2158
2159         color_model = evaluate_color_model(client_byte_order,
2160                 server_byte_order,
2161                 bits_per_pixel);
2162 // Get the color model
2163         switch(color_model)
2164         {
2165                 case BC_RGB8:
2166                         if(private_color) {
2167                                 cmap = XCreateColormap(display, rootwin, vis, AllocNone);
2168                                 create_private_colors();
2169                         }
2170                         else {
2171                                 cmap = DefaultColormap(display, screen);
2172                                 create_shared_colors();
2173                         }
2174
2175                         allocate_color_table();
2176                         break;
2177
2178                 default:
2179                         //cmap = DefaultColormap(display, screen);
2180                         cmap = XCreateColormap(display, rootwin, vis, AllocNone );
2181                         break;
2182         }
2183         return 0;
2184 }
2185
2186 int BC_WindowBase::create_private_colors()
2187 {
2188         int color;
2189         total_colors = 256;
2190
2191         for(int i = 0; i < 255; i++)
2192         {
2193                 color = (i & 0xc0) << 16;
2194                 color += (i & 0x38) << 10;
2195                 color += (i & 0x7) << 5;
2196                 color_table[i][0] = color;
2197         }
2198         create_shared_colors();        // overwrite the necessary colors on the table
2199         return 0;
2200 }
2201
2202
2203 int BC_WindowBase::create_color(int color)
2204 {
2205         if(total_colors == 256)
2206         {
2207 // replace the closest match with an exact match
2208                 color_table[get_color_rgb8(color)][0] = color;
2209         }
2210         else
2211         {
2212 // add the color to the table
2213                 color_table[total_colors][0] = color;
2214                 total_colors++;
2215         }
2216         return 0;
2217 }
2218
2219 int BC_WindowBase::create_shared_colors()
2220 {
2221         create_color(BLACK);
2222         create_color(WHITE);
2223
2224         create_color(LTGREY);
2225         create_color(MEGREY);
2226         create_color(MDGREY);
2227         create_color(DKGREY);
2228
2229         create_color(LTCYAN);
2230         create_color(MECYAN);
2231         create_color(MDCYAN);
2232         create_color(DKCYAN);
2233
2234         create_color(LTGREEN);
2235         create_color(GREEN);
2236         create_color(DKGREEN);
2237
2238         create_color(LTPINK);
2239         create_color(PINK);
2240         create_color(RED);
2241
2242         create_color(LTBLUE);
2243         create_color(BLUE);
2244         create_color(DKBLUE);
2245
2246         create_color(LTYELLOW);
2247         create_color(MEYELLOW);
2248         create_color(MDYELLOW);
2249         create_color(DKYELLOW);
2250
2251         create_color(LTPURPLE);
2252         create_color(MEPURPLE);
2253         create_color(MDPURPLE);
2254         create_color(DKPURPLE);
2255
2256         create_color(FGGREY);
2257         create_color(MNBLUE);
2258         create_color(ORANGE);
2259         create_color(FTGREY);
2260
2261         return 0;
2262 }
2263
2264 Cursor BC_WindowBase::create_grab_cursor()
2265 {
2266         int iw = 23, iw1 = iw-1, iw2 = iw/2;
2267         int ih = 23, ih1 = ih-1, ih2 = ih/2;
2268         VFrame grab(iw,ih,BC_RGB888);
2269         grab.clear_frame();
2270         grab.set_pixel_color(RED);   // fg
2271         grab.draw_smooth(iw2,0,   iw1,0,   iw1,ih2);
2272         grab.draw_smooth(iw1,ih2, iw1,ih1, iw2,ih1);
2273         grab.draw_smooth(iw2,ih1, 0,ih1,   0,ih2);
2274         grab.draw_smooth(0,ih2,   0,0,     iw2,0);
2275         grab.set_pixel_color(WHITE); // bg
2276         grab.draw_line(0,ih2,     iw2-2,ih2);
2277         grab.draw_line(iw2+2,ih2, iw1,ih2);
2278         grab.draw_line(iw2,0,     iw2,ih2-2);
2279         grab.draw_line(iw2,ih2+2, iw2,ih1);
2280
2281         int bpl = (iw+7)/8, isz = bpl * ih;
2282         char img[isz];  memset(img, 0, isz);
2283         char msk[isz];  memset(msk, 0, isz);
2284         unsigned char **rows = grab.get_rows();
2285         for( int iy=0; iy<ih; ++iy ) {
2286                 char *op = img + iy*bpl;
2287                 char *mp = msk + iy*bpl;
2288                 unsigned char *ip = rows[iy];
2289                 for( int ix=0; ix<iw; ++ix,ip+=3 ) {
2290                         if( ip[0] ) mp[ix>>3] |= (1<<(ix&7));
2291                         if( !ip[1] ) op[ix>>3] |= (1<<(ix&7));
2292                 }
2293         }
2294         unsigned long white_pix = WhitePixel(display, screen);
2295         unsigned long black_pix = BlackPixel(display, screen);
2296         Pixmap img_xpm = XCreatePixmapFromBitmapData(display, rootwin,
2297                 img, iw,ih, white_pix,black_pix, 1);
2298         Pixmap msk_xpm = XCreatePixmapFromBitmapData(display, rootwin,
2299                 msk, iw,ih, white_pix,black_pix, 1);
2300
2301         XColor fc, bc;
2302         fc.flags = bc.flags = DoRed | DoGreen | DoBlue;
2303         fc.red = 0xffff; fc.green = fc.blue = 0;  // fg
2304         bc.red = 0xffff; bc.green = 0xffff; bc.blue = 0x0000;     // bg
2305         Cursor cursor = XCreatePixmapCursor(display, img_xpm,msk_xpm, &fc,&bc, iw2,ih2);
2306         XFreePixmap(display, img_xpm);
2307         XFreePixmap(display, msk_xpm);
2308         return cursor;
2309 }
2310
2311 int BC_WindowBase::allocate_color_table()
2312 {
2313         int red, green, blue, color;
2314         XColor col;
2315
2316         for(int i = 0; i < total_colors; i++)
2317         {
2318                 color = color_table[i][0];
2319                 red = (color & 0xFF0000) >> 16;
2320                 green = (color & 0x00FF00) >> 8;
2321                 blue = color & 0xFF;
2322
2323                 col.flags = DoRed | DoGreen | DoBlue;
2324                 col.red   = red<<8   | red;
2325                 col.green = green<<8 | green;
2326                 col.blue  = blue<<8  | blue;
2327
2328                 XAllocColor(display, cmap, &col);
2329                 color_table[i][1] = col.pixel;
2330         }
2331
2332         XInstallColormap(display, cmap);
2333         return 0;
2334 }
2335
2336 int BC_WindowBase::init_window_shape()
2337 {
2338         if(bg_pixmap && bg_pixmap->use_alpha())
2339         {
2340                 XShapeCombineMask(top_level->display,
2341                         this->win, ShapeBounding, 0, 0,
2342                         bg_pixmap->get_alpha(), ShapeSet);
2343         }
2344         return 0;
2345 }
2346
2347
2348 int BC_WindowBase::init_gc()
2349 {
2350         unsigned long gcmask;
2351         gcmask = GCFont | GCGraphicsExposures;
2352
2353         XGCValues gcvalues;
2354         gcvalues.font = mediumfont->fid;        // set the font
2355         gcvalues.graphics_exposures = 0;        // prevent expose events for every redraw
2356         gc = XCreateGC(display, rootwin, gcmask, &gcvalues);
2357
2358 // gcmask = GCCapStyle | GCJoinStyle;
2359 // XGetGCValues(display, gc, gcmask, &gcvalues);
2360 // printf("BC_WindowBase::init_gc %d %d %d\n", __LINE__, gcvalues.cap_style, gcvalues.join_style);
2361         return 0;
2362 }
2363
2364 int BC_WindowBase::init_fonts()
2365 {
2366         if( !(smallfont = XLoadQueryFont(display, _(resources->small_font))) )
2367                 if( !(smallfont = XLoadQueryFont(display, _(resources->small_font2))) )
2368                         smallfont = XLoadQueryFont(display, "fixed");
2369         if( !(mediumfont = XLoadQueryFont(display, _(resources->medium_font))) )
2370                 if( !(mediumfont = XLoadQueryFont(display, _(resources->medium_font2))) )
2371                         mediumfont = XLoadQueryFont(display, "fixed");
2372         if( !(largefont = XLoadQueryFont(display, _(resources->large_font))) )
2373                 if( !(largefont = XLoadQueryFont(display, _(resources->large_font2))) )
2374                         largefont = XLoadQueryFont(display, "fixed");
2375         if( !(bigfont = XLoadQueryFont(display, _(resources->big_font))) )
2376                 if( !(bigfont = XLoadQueryFont(display, _(resources->big_font2))) )
2377                         bigfont = XLoadQueryFont(display, "fixed");
2378
2379         if((clockfont = XLoadQueryFont(display, _(resources->clock_font))) == NULL)
2380                 if((clockfont = XLoadQueryFont(display, _(resources->clock_font2))) == NULL)
2381                         clockfont = XLoadQueryFont(display, "fixed");
2382
2383         init_xft();
2384         if(get_resources()->use_fontset)
2385         {
2386                 char **m, *d;
2387                 int n;
2388
2389 // FIXME: should check the m,d,n values
2390                 smallfontset = XCreateFontSet(display, resources->small_fontset, &m, &n, &d);
2391                 if( !smallfontset )
2392                         smallfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2393                 mediumfontset = XCreateFontSet(display, resources->medium_fontset, &m, &n, &d);
2394                 if( !mediumfontset )
2395                         mediumfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2396                 largefontset = XCreateFontSet(display, resources->large_fontset, &m, &n, &d);
2397                 if( !largefontset )
2398                         largefontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2399                 bigfontset = XCreateFontSet(display, resources->big_fontset, &m, &n, &d);
2400                 if( !bigfontset )
2401                         bigfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2402                 clockfontset = XCreateFontSet(display, resources->clock_fontset, &m, &n, &d);
2403                 if( !clockfontset )
2404                         clockfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2405                 if(clockfontset && bigfontset && largefontset && mediumfontset && smallfontset) {
2406                         curr_fontset = mediumfontset;
2407                         get_resources()->use_fontset = 1;
2408                 }
2409                 else {
2410                         curr_fontset = 0;
2411                         get_resources()->use_fontset = 0;
2412                 }
2413         }
2414
2415         return 0;
2416 }
2417
2418 void BC_WindowBase::init_xft()
2419 {
2420 #ifdef HAVE_XFT
2421         if( !get_resources()->use_xft ) return;
2422 // apparently, xft is not reentrant, more than this is needed
2423 static Mutex xft_init_lock("BC_WindowBase::xft_init_lock", 0);
2424 xft_init_lock.lock("BC_WindowBase::init_xft");
2425         if(!(smallfont_xft =
2426                 (resources->small_font_xft[0] == '-' ?
2427                         xftFontOpenXlfd(display, screen, resources->small_font_xft) :
2428                         xftFontOpenName(display, screen, resources->small_font_xft))) )
2429                 if(!(smallfont_xft =
2430                         xftFontOpenXlfd(display, screen, resources->small_font_xft2)))
2431                         smallfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2432         if(!(mediumfont_xft =
2433                 (resources->medium_font_xft[0] == '-' ?
2434                         xftFontOpenXlfd(display, screen, resources->medium_font_xft) :
2435                         xftFontOpenName(display, screen, resources->medium_font_xft))) )
2436                 if(!(mediumfont_xft =
2437                         xftFontOpenXlfd(display, screen, resources->medium_font_xft2)))
2438                         mediumfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2439         if(!(largefont_xft =
2440                 (resources->large_font_xft[0] == '-' ?
2441                         xftFontOpenXlfd(display, screen, resources->large_font_xft) :
2442                         xftFontOpenName(display, screen, resources->large_font_xft))) )
2443                 if(!(largefont_xft =
2444                         xftFontOpenXlfd(display, screen, resources->large_font_xft2)))
2445                         largefont_xft = xftFontOpenXlfd(display, screen, "fixed");
2446         if(!(bigfont_xft =
2447                 (resources->big_font_xft[0] == '-' ?
2448                         xftFontOpenXlfd(display, screen, resources->big_font_xft) :
2449                         xftFontOpenName(display, screen, resources->big_font_xft))) )
2450                 if(!(bigfont_xft =
2451                         xftFontOpenXlfd(display, screen, resources->big_font_xft2)))
2452                         bigfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2453         if(!(clockfont_xft =
2454                 (resources->clock_font_xft[0] == '-' ?
2455                         xftFontOpenXlfd(display, screen, resources->clock_font_xft) :
2456                         xftFontOpenName(display, screen, resources->clock_font_xft))) )
2457                 clockfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2458
2459
2460         if(!(bold_smallfont_xft =
2461                 (resources->small_b_font_xft[0] == '-' ?
2462                         xftFontOpenXlfd(display, screen, resources->small_b_font_xft) :
2463                         xftFontOpenName(display, screen, resources->small_b_font_xft))) )
2464                 bold_smallfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2465         if(!(bold_mediumfont_xft =
2466                 (resources->medium_b_font_xft[0] == '-' ?
2467                         xftFontOpenXlfd(display, screen, resources->medium_b_font_xft) :
2468                         xftFontOpenName(display, screen, resources->medium_b_font_xft))) )
2469                 bold_mediumfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2470         if(!(bold_largefont_xft =
2471                 (resources->large_b_font_xft[0] == '-' ?
2472                         xftFontOpenXlfd(display, screen, resources->large_b_font_xft) :
2473                         xftFontOpenName(display, screen, resources->large_b_font_xft))) )
2474                 bold_largefont_xft = xftFontOpenXlfd(display, screen, "fixed");
2475
2476         if( !smallfont_xft || !mediumfont_xft || !largefont_xft || !bigfont_xft ||
2477             !bold_largefont_xft || !bold_mediumfont_xft || !bold_largefont_xft ||
2478             !clockfont_xft ) {
2479                 printf("BC_WindowBase::init_fonts: no xft fonts found:"
2480                         " %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n",
2481                         resources->small_font_xft, smallfont_xft,
2482                         resources->medium_font_xft, mediumfont_xft,
2483                         resources->large_font_xft, largefont_xft,
2484                         resources->big_font_xft, bigfont_xft,
2485                         resources->clock_font_xft, clockfont_xft,
2486                         resources->small_b_font_xft, bold_smallfont_xft,
2487                         resources->medium_b_font_xft, bold_mediumfont_xft,
2488                         resources->large_b_font_xft, bold_largefont_xft);
2489                 get_resources()->use_xft = 0;
2490                 exit(1);
2491         }
2492 // _XftDisplayInfo needs a lock.
2493         xftDefaultHasRender(display);
2494 xft_init_lock.unlock();
2495 #endif // HAVE_XFT
2496 }
2497
2498 void BC_WindowBase::init_glyphs()
2499 {
2500 // draw all ascii char glyphs
2501 //  There are problems with some/my graphics boards/drivers
2502 //  which cause some glyphs to be munged if draws occur while
2503 //  the font is being loaded.  This code fills the font caches
2504 //  by drawing all the ascii glyphs before the system starts.
2505 //  Not a fix, but much better than nothing.
2506         static int inited = 0;
2507         if( inited ) return;
2508         XGrabServer(display);
2509         XSync(display, 0);
2510         inited = 1;
2511         int cur_font = current_font;
2512 // locale encodings, needed glyphs to be preloaded
2513         const char *text = _( // ascii 0x20...0x7e
2514                 " !\"#$%&'()*+,-./0123456789:;<=>?"
2515                 "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
2516                 "`abcdefghijklmnopqrstuvwxyz{|}~");
2517         for( int font=SMALLFONT; font<=LARGEFONT; ++font ) {
2518                 set_font(font);
2519                 draw_text(5,5, text);
2520         }
2521         set_font(cur_font);
2522         XUngrabServer(display);
2523 }
2524
2525 void BC_WindowBase::init_im()
2526 {
2527         XIMStyles *xim_styles;
2528         XIMStyle xim_style;
2529
2530         if(!(input_method = XOpenIM(display, NULL, NULL, NULL)))
2531         {
2532                 printf("BC_WindowBase::init_im: Could not open input method.\n");
2533                 XSetLocaleModifiers("@im=local");
2534                 if(!(input_method = XOpenIM(display, NULL, NULL, NULL))) {
2535                 printf("BC_WindowBase::init_im: Could not open input method local.\n");
2536                 exit(1);
2537                 }
2538         }
2539         if(XGetIMValues(input_method, XNQueryInputStyle, &xim_styles, NULL) ||
2540                         xim_styles == NULL)
2541         {
2542                 printf("BC_WindowBase::init_im: Input method doesn't support any styles.\n");
2543                 XCloseIM(input_method);
2544                 exit(1);
2545         }
2546
2547         xim_style = 0;
2548         for(int z = 0;  z < xim_styles->count_styles;  z++)
2549         {
2550                 if(xim_styles->supported_styles[z] == (XIMPreeditNothing | XIMStatusNothing))
2551                 {
2552                         xim_style = xim_styles->supported_styles[z];
2553                         break;
2554                 }
2555         }
2556         XFree(xim_styles);
2557
2558         if(xim_style == 0)
2559         {
2560                 printf("BC_WindowBase::init_im: Input method doesn't support the style we need.\n");
2561                 XCloseIM(input_method);
2562                 exit(1);
2563         }
2564
2565         input_context = XCreateIC(input_method, XNInputStyle, xim_style,
2566                 XNClientWindow, win, XNFocusWindow, win, NULL);
2567         if(!input_context)
2568         {
2569                 printf("BC_WindowBase::init_im: Failed to create input context.\n");
2570                 XCloseIM(input_method);
2571                 exit(1);
2572         }
2573 }
2574
2575 void BC_WindowBase::finit_im()
2576 {
2577         if( input_context ) {
2578                 XDestroyIC(input_context);
2579                 input_context = 0;
2580         }
2581         if( input_method ) {
2582                 XCloseIM(input_method);
2583                 input_method = 0;
2584         }
2585 }
2586
2587
2588 int BC_WindowBase::get_color(int64_t color)
2589 {
2590 // return pixel of color
2591 // use this only for drawing subwindows not for bitmaps
2592          int i, test, difference;
2593
2594         switch(color_model)
2595         {
2596         case BC_RGB8:
2597                 if(private_color)
2598                         return get_color_rgb8(color);
2599 // test last color looked up
2600                 if(current_color_value == color)
2601                         return current_color_pixel;
2602
2603 // look up in table
2604                 current_color_value = color;
2605                 for(i = 0; i < total_colors; i++)
2606                 {
2607                         if(color_table[i][0] == color)
2608                         {
2609                                 current_color_pixel = color_table[i][1];
2610                                 return current_color_pixel;
2611                         }
2612                 }
2613
2614 // find nearest match
2615                 difference = 0xFFFFFF;
2616
2617                 for(i = 0; i < total_colors; i++)
2618                 {
2619                         test = abs((int)(color_table[i][0] - color));
2620
2621                         if(test < difference)
2622                         {
2623                                 current_color_pixel = color_table[i][1];
2624                                 difference = test;
2625                         }
2626                 }
2627                 return current_color_pixel;
2628
2629         case BC_RGB565:
2630                 return get_color_rgb16(color);
2631
2632         case BC_BGR565:
2633                 return get_color_bgr16(color);
2634
2635         case BC_RGB888:
2636         case BC_BGR888:
2637                 return client_byte_order == server_byte_order ?
2638                         color : get_color_bgr24(color);
2639
2640         default:
2641                 return color;
2642         }
2643         return 0;
2644 }
2645
2646 int BC_WindowBase::get_color_rgb8(int color)
2647 {
2648         int pixel;
2649
2650         pixel = (color & 0xc00000) >> 16;
2651         pixel += (color & 0xe000) >> 10;
2652         pixel += (color & 0xe0) >> 5;
2653         return pixel;
2654 }
2655
2656 int64_t BC_WindowBase::get_color_rgb16(int color)
2657 {
2658         int64_t result;
2659         result = (color & 0xf80000) >> 8;
2660         result += (color & 0xfc00) >> 5;
2661         result += (color & 0xf8) >> 3;
2662
2663         return result;
2664 }
2665
2666 int64_t BC_WindowBase::get_color_bgr16(int color)
2667 {
2668         int64_t result;
2669         result = (color & 0xf80000) >> 19;
2670         result += (color & 0xfc00) >> 5;
2671         result += (color & 0xf8) << 8;
2672
2673         return result;
2674 }
2675
2676 int64_t BC_WindowBase::get_color_bgr24(int color)
2677 {
2678         int64_t result;
2679         result = (color & 0xff) << 16;
2680         result += (color & 0xff00);
2681         result += (color & 0xff0000) >> 16;
2682         return result;
2683 }
2684
2685 void BC_WindowBase::start_video()
2686 {
2687         cursor_timer->update();
2688         video_on = 1;
2689 //      set_color(BLACK);
2690 //      draw_box(0, 0, get_w(), get_h());
2691 //      flash();
2692 }
2693
2694 void BC_WindowBase::stop_video()
2695 {
2696         video_on = 0;
2697         unhide_cursor();
2698 }
2699
2700
2701
2702 int64_t BC_WindowBase::get_color()
2703 {
2704         return top_level->current_color;
2705 }
2706
2707 void BC_WindowBase::set_color(int64_t color)
2708 {
2709         top_level->current_color = color;
2710         XSetForeground(top_level->display,
2711                 top_level->gc,
2712                 top_level->get_color(color));
2713 }
2714
2715 void BC_WindowBase::set_opaque()
2716 {
2717         XSetFunction(top_level->display, top_level->gc, GXcopy);
2718 }
2719
2720 void BC_WindowBase::set_inverse()
2721 {
2722         XSetFunction(top_level->display, top_level->gc, GXxor);
2723 }
2724
2725 void BC_WindowBase::set_line_width(int value)
2726 {
2727         this->line_width = value;
2728         XSetLineAttributes(top_level->display, top_level->gc, value,    /* line_width */
2729                 line_dashes == 0 ? LineSolid : LineOnOffDash,           /* line_style */
2730                 line_dashes == 0 ? CapRound : CapNotLast,               /* cap_style */
2731                 JoinMiter);                                             /* join_style */
2732
2733         if(line_dashes > 0) {
2734                 const char dashes = line_dashes;
2735                 XSetDashes(top_level->display, top_level->gc, 0, &dashes, 1);
2736         }
2737
2738 // XGCValues gcvalues;
2739 // unsigned long gcmask;
2740 // gcmask = GCCapStyle | GCJoinStyle;
2741 // XGetGCValues(top_level->display, top_level->gc, gcmask, &gcvalues);
2742 // printf("BC_WindowBase::set_line_width %d %d %d\n", __LINE__, gcvalues.cap_style, gcvalues.join_style);
2743 }
2744
2745 void BC_WindowBase::set_line_dashes(int value)
2746 {
2747         line_dashes = value;
2748 // call XSetLineAttributes
2749         set_line_width(line_width);
2750 }
2751
2752
2753 Cursor BC_WindowBase::get_cursor_struct(int cursor)
2754 {
2755         switch(cursor)
2756         {
2757                 case ARROW_CURSOR:         return top_level->arrow_cursor;
2758                 case CROSS_CURSOR:         return top_level->cross_cursor;
2759                 case IBEAM_CURSOR:         return top_level->ibeam_cursor;
2760                 case VSEPARATE_CURSOR:     return top_level->vseparate_cursor;
2761                 case HSEPARATE_CURSOR:     return top_level->hseparate_cursor;
2762                 case MOVE_CURSOR:          return top_level->move_cursor;
2763                 case LEFT_CURSOR:          return top_level->left_cursor;
2764                 case RIGHT_CURSOR:         return top_level->right_cursor;
2765                 case UPRIGHT_ARROW_CURSOR: return top_level->upright_arrow_cursor;
2766                 case UPLEFT_RESIZE:        return top_level->upleft_resize_cursor;
2767                 case UPRIGHT_RESIZE:       return top_level->upright_resize_cursor;
2768                 case DOWNLEFT_RESIZE:      return top_level->downleft_resize_cursor;
2769                 case DOWNRIGHT_RESIZE:     return top_level->downright_resize_cursor;
2770                 case HOURGLASS_CURSOR:     return top_level->hourglass_cursor;
2771                 case TRANSPARENT_CURSOR:   return top_level->transparent_cursor;
2772                 case GRABBED_CURSOR:       return top_level->grabbed_cursor;
2773         }
2774         return 0;
2775 }
2776
2777 void BC_WindowBase::set_cursor(int cursor, int override, int flush)
2778 {
2779 // inherit cursor from parent
2780         if(cursor < 0)
2781         {
2782                 XUndefineCursor(top_level->display, win);
2783                 current_cursor = cursor;
2784         }
2785         else
2786 // don't change cursor if overridden
2787         if((!top_level->is_hourglass && !is_transparent) ||
2788                 override)
2789         {
2790                 XDefineCursor(top_level->display, win, get_cursor_struct(cursor));
2791                 if(flush) this->flush();
2792         }
2793
2794         if(!override) current_cursor = cursor;
2795 }
2796
2797 void BC_WindowBase::set_x_cursor(int cursor)
2798 {
2799         temp_cursor = XCreateFontCursor(top_level->display, cursor);
2800         XDefineCursor(top_level->display, win, temp_cursor);
2801         current_cursor = cursor;
2802         flush();
2803 }
2804
2805 int BC_WindowBase::get_cursor()
2806 {
2807         return current_cursor;
2808 }
2809
2810 void BC_WindowBase::start_hourglass()
2811 {
2812         top_level->start_hourglass_recursive();
2813         top_level->flush();
2814 }
2815
2816 void BC_WindowBase::stop_hourglass()
2817 {
2818         top_level->stop_hourglass_recursive();
2819         top_level->flush();
2820 }
2821
2822 void BC_WindowBase::start_hourglass_recursive()
2823 {
2824         if(this == top_level)
2825         {
2826                 hourglass_total++;
2827                 is_hourglass = 1;
2828         }
2829
2830         if(!is_transparent)
2831         {
2832                 set_cursor(HOURGLASS_CURSOR, 1, 0);
2833                 for(int i = 0; i < subwindows->total; i++)
2834                 {
2835                         subwindows->values[i]->start_hourglass_recursive();
2836                 }
2837         }
2838 }
2839
2840 void BC_WindowBase::stop_hourglass_recursive()
2841 {
2842         if(this == top_level)
2843         {
2844                 if(hourglass_total == 0) return;
2845                 top_level->hourglass_total--;
2846         }
2847
2848         if(!top_level->hourglass_total)
2849         {
2850                 top_level->is_hourglass = 0;
2851
2852 // Cause set_cursor to perform change
2853                 if(!is_transparent)
2854                         set_cursor(current_cursor, 1, 0);
2855
2856                 for(int i = 0; i < subwindows->total; i++)
2857                 {
2858                         subwindows->values[i]->stop_hourglass_recursive();
2859                 }
2860         }
2861 }
2862
2863
2864
2865
2866 XFontStruct* BC_WindowBase::get_font_struct(int font)
2867 {
2868 // Clear out unrelated flags
2869         if(font & BOLDFACE) font ^= BOLDFACE;
2870
2871         switch(font) {
2872                 case SMALLFONT:  return top_level->smallfont;  break;
2873                 case MEDIUMFONT: return top_level->mediumfont; break;
2874                 case LARGEFONT:  return top_level->largefont;  break;
2875                 case BIGFONT:    return top_level->bigfont;    break;
2876                 case CLOCKFONT:  return top_level->clockfont;  break;
2877         }
2878         return 0;
2879 }
2880
2881 XFontSet BC_WindowBase::get_fontset(int font)
2882 {
2883         XFontSet fs = 0;
2884
2885         if(get_resources()->use_fontset)
2886         {
2887                 switch(font & 0xff) {
2888                         case SMALLFONT:  fs = top_level->smallfontset; break;
2889                         case MEDIUMFONT: fs = top_level->mediumfontset; break;
2890                         case LARGEFONT:  fs = top_level->largefontset; break;
2891                         case BIGFONT:    fs = top_level->bigfontset;   break;
2892                         case CLOCKFONT: fs = top_level->clockfontset; break;
2893                 }
2894         }
2895
2896         return fs;
2897 }
2898
2899 #ifdef HAVE_XFT
2900 XftFont* BC_WindowBase::get_xft_struct(int font)
2901 {
2902         switch(font) {
2903                 case SMALLFONT:    return (XftFont*)top_level->smallfont_xft;
2904                 case MEDIUMFONT:   return (XftFont*)top_level->mediumfont_xft;
2905                 case LARGEFONT:    return (XftFont*)top_level->largefont_xft;
2906                 case BIGFONT:      return (XftFont*)top_level->bigfont_xft;
2907                 case CLOCKFONT:    return (XftFont*)top_level->clockfont_xft;
2908                 case MEDIUMFONT_3D: return (XftFont*)top_level->bold_mediumfont_xft;
2909                 case SMALLFONT_3D:  return (XftFont*)top_level->bold_smallfont_xft;
2910                 case LARGEFONT_3D:  return (XftFont*)top_level->bold_largefont_xft;
2911         }
2912
2913         return 0;
2914 }
2915 #endif
2916
2917
2918 int BC_WindowBase::get_current_font()
2919 {
2920         return top_level->current_font;
2921 }
2922
2923 void BC_WindowBase::set_font(int font)
2924 {
2925         top_level->current_font = font;
2926
2927 #ifdef HAVE_XFT
2928         if(get_resources()->use_xft) {}
2929         else
2930 #endif
2931         if(get_resources()->use_fontset) {
2932                 set_fontset(font);
2933         }
2934
2935         if(get_font_struct(font))
2936         {
2937                 XSetFont(top_level->display, top_level->gc, get_font_struct(font)->fid);
2938         }
2939
2940         return;
2941 }
2942
2943 void BC_WindowBase::set_fontset(int font)
2944 {
2945         XFontSet fs = 0;
2946
2947         if(get_resources()->use_fontset) {
2948                 switch(font) {
2949                         case SMALLFONT:  fs = top_level->smallfontset; break;
2950                         case MEDIUMFONT: fs = top_level->mediumfontset; break;
2951                         case LARGEFONT:  fs = top_level->largefontset; break;
2952                         case BIGFONT:    fs = top_level->bigfontset;   break;
2953                         case CLOCKFONT:  fs = top_level->clockfontset; break;
2954                 }
2955         }
2956
2957         curr_fontset = fs;
2958 }
2959
2960
2961 XFontSet BC_WindowBase::get_curr_fontset(void)
2962 {
2963         if(get_resources()->use_fontset)
2964                 return curr_fontset;
2965         return 0;
2966 }
2967
2968 int BC_WindowBase::get_single_text_width(int font, const char *text, int length)
2969 {
2970 #ifdef HAVE_XFT
2971         if(get_resources()->use_xft && get_xft_struct(font))
2972         {
2973                 XGlyphInfo extents;
2974 #ifdef X_HAVE_UTF8_STRING
2975                 if(get_resources()->locale_utf8)
2976                 {
2977                         xftTextExtentsUtf8(top_level->display,
2978                                 get_xft_struct(font),
2979                                 (const XftChar8 *)text,
2980                                 length,
2981                                 &extents);
2982                 }
2983                 else
2984 #endif
2985                 {
2986                         xftTextExtents8(top_level->display,
2987                                 get_xft_struct(font),
2988                                 (const XftChar8 *)text,
2989                                 length,
2990                                 &extents);
2991                 }
2992                 return extents.xOff;
2993         }
2994         else
2995 #endif
2996         if(get_resources()->use_fontset && top_level->get_fontset(font))
2997                 return XmbTextEscapement(top_level->get_fontset(font), text, length);
2998         else
2999         if(get_font_struct(font))
3000                 return XTextWidth(get_font_struct(font), text, length);
3001         else
3002         {
3003                 int w = 0;
3004                 switch(font)
3005                 {
3006                         case MEDIUM_7SEGMENT:
3007                                 return get_resources()->medium_7segment[0]->get_w() * length;
3008                                 break;
3009
3010                         default:
3011                                 return 0;
3012                 }
3013                 return w;
3014         }
3015 }
3016
3017 int BC_WindowBase::get_text_width(int font, const char *text, int length)
3018 {
3019         int i, j, w = 0, line_w = 0;
3020         if(length < 0) length = strlen(text);
3021
3022         for(i = 0, j = 0; i <= length; i++)
3023         {
3024                 line_w = 0;
3025                 if(text[i] == '\n')
3026                 {
3027                         line_w = get_single_text_width(font, &text[j], i - j);
3028                         j = i + 1;
3029                 }
3030                 else
3031                 if(text[i] == 0)
3032                 {
3033                         line_w = get_single_text_width(font, &text[j], length - j);
3034                 }
3035                 if(line_w > w) w = line_w;
3036         }
3037
3038         if(i > length && w == 0)
3039         {
3040                 w = get_single_text_width(font, text, length);
3041         }
3042
3043         return w;
3044 }
3045
3046 int BC_WindowBase::get_text_width(int font, const wchr_t *text, int length)
3047 {
3048         int i, j, w = 0;
3049         if( length < 0 ) length = wstrlen(text);
3050
3051         for( i=j=0; i<length && text[i]; ++i ) {
3052                 if( text[i] != '\n' ) continue;
3053                 if( i > j ) {
3054                         int lw = get_single_text_width(font, &text[j], i-j);
3055                         if( w < lw ) w = lw;
3056                 }
3057                 j = i + 1;
3058         }
3059         if( j < length ) {
3060                 int lw = get_single_text_width(font, &text[j], length-j);
3061                 if( w < lw ) w = lw;
3062         }
3063
3064         return w;
3065 }
3066
3067 int BC_WindowBase::get_text_ascent(int font)
3068 {
3069 #ifdef HAVE_XFT
3070         XftFont *fstruct;
3071         if( (fstruct = get_xft_struct(font)) != 0 )
3072                 return fstruct->ascent;
3073 #endif
3074         if(get_resources()->use_fontset && top_level->get_fontset(font))
3075         {
3076                 XFontSetExtents *extents;
3077
3078                 extents = XExtentsOfFontSet(top_level->get_fontset(font));
3079                 return -extents->max_logical_extent.y;
3080         }
3081
3082         if(get_font_struct(font))
3083                 return top_level->get_font_struct(font)->ascent;
3084
3085         switch(font) {
3086                 case MEDIUM_7SEGMENT:
3087                         return get_resources()->medium_7segment[0]->get_h();
3088         }
3089         return 0;
3090 }
3091
3092 int BC_WindowBase::get_text_descent(int font)
3093 {
3094 #ifdef HAVE_XFT
3095         XftFont *fstruct;
3096         if( (fstruct = get_xft_struct(font)) != 0 )
3097                 return fstruct->descent;
3098 #endif
3099         if(get_resources()->use_fontset && top_level->get_fontset(font)) {
3100                 XFontSetExtents *extents;
3101                 extents = XExtentsOfFontSet(top_level->get_fontset(font));
3102                 return (extents->max_logical_extent.height
3103                         + extents->max_logical_extent.y);
3104         }
3105
3106         if(get_font_struct(font))
3107                 return top_level->get_font_struct(font)->descent;
3108
3109         return 0;
3110 }
3111
3112 int BC_WindowBase::get_text_height(int font, const char *text)
3113 {
3114         int rowh;
3115 #ifdef HAVE_XFT
3116         XftFont *fstruct;
3117         if( (fstruct = get_xft_struct(font)) != 0 )
3118                 rowh = fstruct->height;
3119         else
3120 #endif
3121                 rowh = get_text_ascent(font) + get_text_descent(font);
3122
3123         if(!text) return rowh;
3124
3125 // Add height of lines
3126         int h = 0, i, length = strlen(text);
3127         for(i = 0; i <= length; i++)
3128         {
3129                 if(text[i] == '\n')
3130                         h++;
3131                 else
3132                 if(text[i] == 0)
3133                         h++;
3134         }
3135         return h * rowh;
3136 }
3137
3138 // truncate the text with ... & return a new string
3139 char *BC_WindowBase::get_truncated_text(int font, const char *text, int max_w)
3140 {
3141         char *result = cstrdup(text);
3142         int text_w = get_text_width(font, text);
3143         int ci = -1, len = strlen(text);
3144         if( text_w > max_w ) {
3145 // get center of string
3146                 int cx = text_w/2, best = INT_MAX;
3147                 for( int i=1; i<=len; ++i ) {
3148                         int cw = get_text_width(font, text, i);
3149                         if( abs(cw-cx) < abs(best-cx) ) {
3150                                 best = cw;  ci = i;
3151                         }
3152                 }
3153         }
3154         if( ci > 0 && ci < len-1 ) {
3155 // insert ... in the center
3156                 result[ci-1] = result[ci] = result[ci+1] = '.';
3157
3158                 while( ci-2>=0 && ci+2<(int)strlen(result) &&
3159                        get_text_width(font, result) > max_w ) {
3160 // take away a character from the longer side
3161                         int left_w = get_text_width(font, result, ci-2);
3162                         int right_w = get_text_width(font, result + ci+3);
3163                         int i = left_w > right_w ? --ci-1 : ci+2;
3164                         while( (result[i]=result[i+1])!=0 ) ++i;
3165                 }
3166         }
3167
3168         return result;
3169 }
3170
3171 BC_Bitmap* BC_WindowBase::new_bitmap(int w, int h, int color_model)
3172 {
3173         if(color_model < 0) color_model = top_level->get_color_model();
3174         return new BC_Bitmap(top_level, w, h, color_model);
3175 }
3176
3177 void BC_WindowBase::init_wait()
3178 {
3179 #ifndef SINGLE_THREAD
3180         if(window_type != MAIN_WINDOW)
3181                 top_level->init_wait();
3182         init_lock->lock("BC_WindowBase::init_wait");
3183         init_lock->unlock();
3184 #endif
3185 }
3186
3187 int BC_WindowBase::accel_available(int color_model, int lock_it)
3188 {
3189         if( window_type != MAIN_WINDOW )
3190                 return top_level->accel_available(color_model, lock_it);
3191         if( lock_it )
3192                 lock_window("BC_WindowBase::accel_available");
3193
3194         switch(color_model) {
3195         case BC_YUV420P:
3196                 grab_port_id(color_model);
3197                 break;
3198
3199         case BC_YUV422:
3200                 grab_port_id(color_model);
3201                 break;
3202