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