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