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