1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim:expandtab:shiftwidth=2:tabstop=2: */ 3 4 /* ***** BEGIN LICENSE BLOCK ***** 5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 6 * 7 * The contents of this file are subject to the Mozilla Public License Version 8 * 1.1 (the "License"); you may not use this file except in compliance with 9 * the License. You may obtain a copy of the License at 10 * http://www.mozilla.org/MPL/ 11 * 12 * Software distributed under the License is distributed on an "AS IS" basis, 13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 14 * for the specific language governing rights and limitations under the 15 * License. 16 * 17 * The Original Code is the Gtk2XtBin Widget Implementation. 18 * 19 * The Initial Developer of the Original Code is 20 * Sun Microsystems, Inc. 21 * Portions created by the Initial Developer are Copyright (C) 2002 22 * the Initial Developer. All Rights Reserved. 23 * 24 * Contributor(s): 25 * 26 * Alternatively, the contents of this file may be used under the terms of 27 * either the GNU General Public License Version 2 or later (the "GPL"), or 28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 29 * in which case the provisions of the GPL or the LGPL are applicable instead 30 * of those above. If you wish to allow use of your version of this file only 31 * under the terms of either the GPL or the LGPL, and not to allow others to 32 * use your version of this file under the terms of the MPL, indicate your 33 * decision by deleting the provisions above and replace them with the notice 34 * and other provisions required by the GPL or the LGPL. If you do not delete 35 * the provisions above, a recipient may use your version of this file under 36 * the terms of any one of the MPL, the GPL or the LGPL. 37 * 38 * ***** END LICENSE BLOCK ***** */ 39 40 /* 41 * The GtkXtBin widget allows for Xt toolkit code to be used 42 * inside a GTK application. 43 */ 44 45 #include "xembed.h" 46 #include "gtk2xtbin.h" 47 #include <gtk/gtk.h> 48 #include <gdk/gdkx.h> 49 #include <glib.h> 50 #include <assert.h> 51 #include <sys/time.h> 52 #include <sys/types.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <unistd.h> 56 57 /* Xlib/Xt stuff */ 58 #include <X11/Xlib.h> 59 #include <X11/Xutil.h> 60 #include <X11/Shell.h> 61 #include <X11/Intrinsic.h> 62 #include <X11/StringDefs.h> 63 64 /* uncomment this if you want debugging information about widget 65 creation and destruction */ 66 #undef DEBUG_XTBIN 67 68 #define XTBIN_MAX_EVENTS 30 69 70 static void gtk_xtbin_class_init (GtkXtBinClass *klass); 71 static void gtk_xtbin_init (GtkXtBin *xtbin); 72 static void gtk_xtbin_realize (GtkWidget *widget); 73 static void gtk_xtbin_unrealize (GtkWidget *widget); 74 static void gtk_xtbin_destroy (GtkObject *object); 75 static void gtk_xtbin_shutdown (GtkObject *object); 76 77 /* Xt aware XEmbed */ 78 static void xt_client_init (XtClient * xtclient, 79 Visual *xtvisual, 80 Colormap xtcolormap, 81 int xtdepth); 82 static void xt_client_create (XtClient * xtclient, 83 Window embeder, 84 int height, 85 int width ); 86 static void xt_client_unrealize (XtClient* xtclient); 87 static void xt_client_destroy (XtClient* xtclient); 88 static void xt_client_set_info (Widget xtplug, 89 unsigned long flags); 90 static void xt_client_event_handler (Widget w, 91 XtPointer client_data, 92 XEvent *event); 93 static void xt_client_handle_xembed_message (Widget w, 94 XtPointer client_data, 95 XEvent *event); 96 static void xt_client_focus_listener (Widget w, 97 XtPointer user_data, 98 XEvent *event); 99 static void xt_add_focus_listener( Widget w, XtPointer user_data ); 100 static void xt_add_focus_listener_tree ( Widget treeroot, XtPointer user_data); 101 static void xt_remove_focus_listener(Widget w, XtPointer user_data); 102 static void send_xembed_message (XtClient *xtclient, 103 long message, 104 long detail, 105 long data1, 106 long data2, 107 long time); 108 static int error_handler (Display *display, 109 XErrorEvent *error); 110 /* For error trap of XEmbed */ 111 static void trap_errors(void); 112 static int untrap_error(void); 113 static int (*old_error_handler) (Display *, XErrorEvent *); 114 static int trapped_error_code = 0; 115 116 static GtkWidgetClass *parent_class = NULL; 117 118 static Display *xtdisplay = NULL; 119 static String *fallback = NULL; 120 static gboolean xt_is_initialized = FALSE; 121 static gint num_widgets = 0; 122 123 static GPollFD xt_event_poll_fd; 124 static gint xt_polling_timer_id = 0; 125 static guint tag = 0; 126 127 static gboolean 128 xt_event_prepare (GSource* source_data, 129 gint *timeout) 130 { 131 int mask; 132 133 GDK_THREADS_ENTER(); 134 mask = XPending(xtdisplay); 135 GDK_THREADS_LEAVE(); 136 137 return (gboolean)mask; 138 } 139 140 static gboolean 141 xt_event_check (GSource* source_data) 142 { 143 GDK_THREADS_ENTER (); 144 145 if (xt_event_poll_fd.revents & G_IO_IN) { 146 int mask; 147 mask = XPending(xtdisplay); 148 GDK_THREADS_LEAVE (); 149 return (gboolean)mask; 150 } 151 152 GDK_THREADS_LEAVE (); 153 return FALSE; 154 } 155 156 static gboolean 157 xt_event_dispatch (GSource* source_data, 158 GSourceFunc call_back, 159 gpointer user_data) 160 { 161 XEvent event; 162 XtAppContext ac; 163 int i = 0; 164 165 ac = XtDisplayToApplicationContext(xtdisplay); 166 167 GDK_THREADS_ENTER (); 168 169 /* Process only real X traffic here. We only look for data on the 170 * pipe, limit it to XTBIN_MAX_EVENTS and only call 171 * XtAppProcessEvent so that it will look for X events. There's no 172 * timer processing here since we already have a timer callback that 173 * does it. */ 174 for (i=0; i < XTBIN_MAX_EVENTS && XPending(xtdisplay); i++) { 175 XtAppProcessEvent(ac, XtIMXEvent); 176 } 177 178 GDK_THREADS_LEAVE (); 179 180 return TRUE; 181 } 182 183 static GSourceFuncs xt_event_funcs = { 184 xt_event_prepare, 185 xt_event_check, 186 xt_event_dispatch, 187 g_free, 188 (GSourceFunc)NULL, 189 (GSourceDummyMarshal)NULL 190 }; 191 192 static gboolean 193 xt_event_polling_timer_callback(gpointer user_data) 194 { 195 Display * display; 196 XtAppContext ac; 197 int eventsToProcess = 20; 198 199 display = (Display *)user_data; 200 ac = XtDisplayToApplicationContext(display); 201 202 /* We need to process many Xt events here. If we just process 203 one event we might starve one or more Xt consumers. On the other hand 204 this could hang the whole app if Xt events come pouring in. So process 205 up to 20 Xt events right now and save the rest for later. This is a hack, 206 but it oughta work. We *really* should have out of process plugins. 207 */ 208 while (eventsToProcess-- && XtAppPending(ac)) 209 XtAppProcessEvent(ac, XtIMAll); 210 return TRUE; 211 } 212 213 GType 214 gtk_xtbin_get_type (void) 215 { 216 static GType xtbin_type = 0; 217 218 if (!xtbin_type) { 219 static const GTypeInfo xtbin_info = 220 { 221 sizeof (GtkXtBinClass), 222 NULL, 223 NULL, 224 225 (GClassInitFunc)gtk_xtbin_class_init, 226 NULL, 227 NULL, 228 229 sizeof (GtkXtBin), 230 0, 231 (GInstanceInitFunc)gtk_xtbin_init, 232 }; 233 xtbin_type = g_type_register_static (GTK_TYPE_SOCKET, 234 "GtkXtBin", 235 &xtbin_info, 236 0); 237 } 238 return xtbin_type; 239 } 240 241 static void 242 gtk_xtbin_class_init (GtkXtBinClass *klass) 243 { 244 GtkWidgetClass *widget_class; 245 GtkObjectClass *object_class; 246 247 parent_class = g_type_class_peek_parent (klass); 248 249 widget_class = GTK_WIDGET_CLASS (klass); 250 widget_class->realize = gtk_xtbin_realize; 251 widget_class->unrealize = gtk_xtbin_unrealize; 252 253 object_class = GTK_OBJECT_CLASS (klass); 254 object_class->destroy = gtk_xtbin_destroy; 255 } 256 257 static void 258 gtk_xtbin_init (GtkXtBin *xtbin) 259 { 260 xtbin->xtdisplay = NULL; 261 xtbin->parent_window = NULL; 262 xtbin->xtwindow = 0; 263 xtbin->x = 0; 264 xtbin->y = 0; 265 } 266 267 static void 268 gtk_xtbin_realize (GtkWidget *widget) 269 { 270 GtkXtBin *xtbin; 271 GtkAllocation allocation = { 0, 0, 200, 200 }; 272 gint x, y, w, h, d; /* geometry of window */ 273 274 #ifdef DEBUG_XTBIN 275 printf("gtk_xtbin_realize()\n"); 276 #endif 277 278 g_return_if_fail (GTK_IS_XTBIN (widget)); 279 280 xtbin = GTK_XTBIN (widget); 281 282 /* caculate the allocation before realize */ 283 gdk_window_get_geometry(xtbin->parent_window, &x, &y, &w, &h, &d); 284 allocation.width = w; 285 allocation.height = h; 286 gtk_widget_size_allocate (widget, &allocation); 287 288 #ifdef DEBUG_XTBIN 289 printf("initial allocation %d %d %d %d\n", x, y, w, h); 290 #endif 291 292 xtbin->width = widget->allocation.width; 293 xtbin->height = widget->allocation.height; 294 295 /* use GtkSocket's realize */ 296 (*GTK_WIDGET_CLASS(parent_class)->realize)(widget); 297 298 /* create the Xt client widget */ 299 xt_client_create(&(xtbin->xtclient), 300 gtk_socket_get_id(GTK_SOCKET(xtbin)), 301 xtbin->height, 302 xtbin->width); 303 xtbin->xtwindow = XtWindow(xtbin->xtclient.child_widget); 304 305 gdk_flush(); 306 307 /* now that we have created the xt client, add it to the socket. */ 308 gtk_socket_add_id(GTK_SOCKET(widget), xtbin->xtwindow); 309 } 310 311 312 313 GtkWidget* 314 gtk_xtbin_new (GdkWindow *parent_window, String * f) 315 { 316 GtkXtBin *xtbin; 317 gpointer user_data; 318 319 assert(parent_window != NULL); 320 xtbin = g_object_new (GTK_TYPE_XTBIN, NULL); 321 322 if (!xtbin) 323 return (GtkWidget*)NULL; 324 325 if (f) 326 fallback = f; 327 328 /* Initialize the Xt toolkit */ 329 xtbin->parent_window = parent_window; 330 331 xt_client_init(&(xtbin->xtclient), 332 GDK_VISUAL_XVISUAL(gdk_rgb_get_visual()), 333 GDK_COLORMAP_XCOLORMAP(gdk_rgb_get_colormap()), 334 gdk_rgb_get_visual()->depth); 335 336 if (!xtbin->xtclient.xtdisplay) { 337 /* If XtOpenDisplay failed, we can't go any further. 338 * Bail out. 339 */ 340 #ifdef DEBUG_XTBIN 341 printf("gtk_xtbin_init: XtOpenDisplay() returned NULL.\n"); 342 #endif 343 g_free (xtbin); 344 return (GtkWidget *)NULL; 345 } 346 347 /* If this is the first running widget, hook this display into the 348 mainloop */ 349 if (0 == num_widgets) { 350 int cnumber; 351 /* 352 * hook Xt event loop into the glib event loop. 353 */ 354 355 /* the assumption is that gtk_init has already been called */ 356 GSource* gs = g_source_new(&xt_event_funcs, sizeof(GSource)); 357 if (!gs) { 358 return NULL; 359 } 360 361 g_source_set_priority(gs, GDK_PRIORITY_EVENTS); 362 g_source_set_can_recurse(gs, TRUE); 363 tag = g_source_attach(gs, (GMainContext*)NULL); 364 #ifdef VMS 365 cnumber = XConnectionNumber(xtdisplay); 366 #else 367 cnumber = ConnectionNumber(xtdisplay); 368 #endif 369 xt_event_poll_fd.fd = cnumber; 370 xt_event_poll_fd.events = G_IO_IN; 371 xt_event_poll_fd.revents = 0; /* hmm... is this correct? */ 372 373 g_main_context_add_poll ((GMainContext*)NULL, 374 &xt_event_poll_fd, 375 G_PRIORITY_LOW); 376 /* add a timer so that we can poll and process Xt timers */ 377 xt_polling_timer_id = 378 g_timeout_add(25, 379 (GSourceFunc)xt_event_polling_timer_callback, 380 xtdisplay); 381 } 382 383 /* Bump up our usage count */ 384 num_widgets++; 385 386 /* Build the hierachy */ 387 xtbin->xtdisplay = xtbin->xtclient.xtdisplay; 388 gtk_widget_set_parent_window(GTK_WIDGET(xtbin), parent_window); 389 gdk_window_get_user_data(xtbin->parent_window, &user_data); 390 if (user_data) 391 gtk_container_add(GTK_CONTAINER(user_data), GTK_WIDGET(xtbin)); 392 393 return GTK_WIDGET (xtbin); 394 } 395 396 void 397 gtk_xtbin_set_position (GtkXtBin *xtbin, 398 gint x, 399 gint y) 400 { 401 xtbin->x = x; 402 xtbin->y = y; 403 404 if (GTK_WIDGET_REALIZED (xtbin)) 405 gdk_window_move (GTK_WIDGET (xtbin)->window, x, y); 406 } 407 408 void 409 gtk_xtbin_resize (GtkWidget *widget, 410 gint width, 411 gint height) 412 { 413 Arg args[2]; 414 GtkXtBin *xtbin = GTK_XTBIN (widget); 415 GtkAllocation allocation; 416 417 #ifdef DEBUG_XTBIN 418 printf("gtk_xtbin_resize %p %d %d\n", (void *)widget, width, height); 419 #endif 420 421 xtbin->height = height; 422 xtbin->width = width; 423 424 // Avoid BadValue errors in XtSetValues 425 if (height <= 0 || width <=0) { 426 height = 1; 427 width = 1; 428 } 429 XtSetArg(args[0], XtNheight, height); 430 XtSetArg(args[1], XtNwidth, width); 431 XtSetValues(xtbin->xtclient.top_widget, args, 2); 432 433 /* we need to send a size allocate so the socket knows about the 434 size changes */ 435 allocation.x = xtbin->x; 436 allocation.y = xtbin->y; 437 allocation.width = xtbin->width; 438 allocation.height = xtbin->height; 439 440 gtk_widget_size_allocate(widget, &allocation); 441 } 442 443 static void 444 gtk_xtbin_unrealize (GtkWidget *object) 445 { 446 GtkXtBin *xtbin; 447 GtkWidget *widget; 448 449 #ifdef DEBUG_XTBIN 450 printf("gtk_xtbin_unrealize()\n"); 451 #endif 452 453 /* gtk_object_destroy() will already hold a refcount on object 454 */ 455 xtbin = GTK_XTBIN(object); 456 widget = GTK_WIDGET(object); 457 458 GTK_WIDGET_UNSET_FLAGS (widget, GTK_VISIBLE); 459 if (GTK_WIDGET_REALIZED (widget)) { 460 xt_client_unrealize(&(xtbin->xtclient)); 461 } 462 463 (*GTK_WIDGET_CLASS (parent_class)->unrealize)(widget); 464 } 465 466 static void 467 gtk_xtbin_destroy (GtkObject *object) 468 { 469 GtkXtBin *xtbin; 470 471 #ifdef DEBUG_XTBIN 472 printf("gtk_xtbin_destroy()\n"); 473 #endif 474 475 g_return_if_fail (object != NULL); 476 g_return_if_fail (GTK_IS_XTBIN (object)); 477 478 xtbin = GTK_XTBIN (object); 479 480 if(xtbin->xtwindow) { 481 /* remove the event handler */ 482 xt_client_destroy(&(xtbin->xtclient)); 483 xtbin->xtwindow = 0; 484 485 num_widgets--; /* reduce our usage count */ 486 487 /* If this is the last running widget, remove the Xt display 488 connection from the mainloop */ 489 if (0 == num_widgets) { 490 #ifdef DEBUG_XTBIN 491 printf("removing the Xt connection from the main loop\n"); 492 #endif 493 g_main_context_remove_poll((GMainContext*)NULL, &xt_event_poll_fd); 494 g_source_remove(tag); 495 496 g_source_remove(xt_polling_timer_id); 497 xt_polling_timer_id = 0; 498 } 499 } 500 501 GTK_OBJECT_CLASS(parent_class)->destroy(object); 502 } 503 504 /* 505 * Following is the implementation of Xt XEmbedded for client side 506 */ 507 508 /* Initial Xt plugin */ 509 static void 510 xt_client_init( XtClient * xtclient, 511 Visual *xtvisual, 512 Colormap xtcolormap, 513 int xtdepth) 514 { 515 XtAppContext app_context; 516 char *mArgv[1]; 517 int mArgc = 0; 518 519 /* 520 * Initialize Xt stuff 521 */ 522 xtclient->top_widget = NULL; 523 xtclient->child_widget = NULL; 524 xtclient->xtdisplay = NULL; 525 xtclient->xtvisual = NULL; 526 xtclient->xtcolormap = 0; 527 xtclient->xtdepth = 0; 528 529 if (!xt_is_initialized) { 530 #ifdef DEBUG_XTBIN 531 printf("starting up Xt stuff\n"); 532 #endif 533 XtToolkitInitialize(); 534 app_context = XtCreateApplicationContext(); 535 if (fallback) 536 XtAppSetFallbackResources(app_context, fallback); 537 538 xtdisplay = XtOpenDisplay(app_context, gdk_get_display(), NULL, 539 "Wrapper", NULL, 0, &mArgc, mArgv); 540 if (xtdisplay) 541 xt_is_initialized = TRUE; 542 } 543 xtclient->xtdisplay = xtdisplay; 544 xtclient->xtvisual = xtvisual; 545 xtclient->xtcolormap = xtcolormap; 546 xtclient->xtdepth = xtdepth; 547 } 548 549 /* Create the Xt client widgets 550 * */ 551 static void 552 xt_client_create ( XtClient* xtclient , 553 Window embedderid, 554 int height, 555 int width ) 556 { 557 int n; 558 Arg args[6]; 559 Widget child_widget; 560 Widget top_widget; 561 562 #ifdef DEBUG_XTBIN 563 printf("xt_client_create() \n"); 564 #endif 565 top_widget = XtAppCreateShell("drawingArea", "Wrapper", 566 applicationShellWidgetClass, 567 xtclient->xtdisplay, 568 NULL, 0); 569 xtclient->top_widget = top_widget; 570 571 /* set size of Xt window */ 572 n = 0; 573 XtSetArg(args[n], XtNheight, height);n++; 574 XtSetArg(args[n], XtNwidth, width);n++; 575 XtSetValues(top_widget, args, n); 576 577 child_widget = XtVaCreateWidget("form", 578 compositeWidgetClass, 579 top_widget, NULL); 580 581 n = 0; 582 XtSetArg(args[n], XtNheight, height);n++; 583 XtSetArg(args[n], XtNwidth, width);n++; 584 XtSetArg(args[n], XtNvisual, xtclient->xtvisual ); n++; 585 XtSetArg(args[n], XtNdepth, xtclient->xtdepth ); n++; 586 XtSetArg(args[n], XtNcolormap, xtclient->xtcolormap ); n++; 587 XtSetArg(args[n], XtNborderWidth, 0); n++; 588 XtSetValues(child_widget, args, n); 589 590 XSync(xtclient->xtdisplay, FALSE); 591 xtclient->oldwindow = top_widget->core.window; 592 top_widget->core.window = embedderid; 593 594 /* this little trick seems to finish initializing the widget */ 595 #if XlibSpecificationRelease >= 6 596 XtRegisterDrawable(xtclient->xtdisplay, 597 embedderid, 598 top_widget); 599 #else 600 _XtRegisterWindow( embedderid, 601 top_widget); 602 #endif 603 XtRealizeWidget(child_widget); 604 605 /* listen to all Xt events */ 606 XSelectInput(xtclient->xtdisplay, 607 XtWindow(top_widget), 608 0x0FFFFF); 609 xt_client_set_info (child_widget, 0); 610 611 XtManageChild(child_widget); 612 xtclient->child_widget = child_widget; 613 614 /* set the event handler */ 615 XtAddEventHandler(child_widget, 616 0x0FFFFF & ~ResizeRedirectMask, 617 TRUE, 618 (XtEventHandler)xt_client_event_handler, xtclient); 619 XtAddEventHandler(child_widget, 620 SubstructureNotifyMask | ButtonReleaseMask, 621 TRUE, 622 (XtEventHandler)xt_client_focus_listener, 623 xtclient); 624 XSync(xtclient->xtdisplay, FALSE); 625 } 626 627 static void 628 xt_client_unrealize ( XtClient* xtclient ) 629 { 630 #if XlibSpecificationRelease >= 6 631 XtUnregisterDrawable(xtclient->xtdisplay, 632 xtclient->top_widget->core.window); 633 #else 634 _XtUnregisterWindow(xtclient->top_widget->core.window, 635 xtclient->top_widget); 636 #endif 637 638 /* flush the queue before we returning origin top_widget->core.window 639 or we can get X error since the window is gone */ 640 XSync(xtclient->xtdisplay, False); 641 642 xtclient->top_widget->core.window = xtclient->oldwindow; 643 XtUnrealizeWidget(xtclient->top_widget); 644 } 645 646 static void 647 xt_client_destroy (XtClient* xtclient) 648 { 649 if(xtclient->top_widget) { 650 XtRemoveEventHandler(xtclient->child_widget, 0x0FFFFF, TRUE, 651 (XtEventHandler)xt_client_event_handler, xtclient); 652 XtDestroyWidget(xtclient->top_widget); 653 xtclient->top_widget = NULL; 654 } 655 } 656 657 static void 658 xt_client_set_info (Widget xtplug, unsigned long flags) 659 { 660 unsigned long buffer[2]; 661 662 Atom infoAtom = XInternAtom(XtDisplay(xtplug), "_XEMBED_INFO", False); 663 664 buffer[1] = 0; /* Protocol version */ 665 buffer[1] = flags; 666 667 XChangeProperty (XtDisplay(xtplug), XtWindow(xtplug), 668 infoAtom, infoAtom, 32, 669 PropModeReplace, 670 (unsigned char *)buffer, 2); 671 } 672 673 static void 674 xt_client_handle_xembed_message(Widget w, XtPointer client_data, XEvent *event) 675 { 676 XtClient *xtplug = (XtClient*)client_data; 677 switch (event->xclient.data.l[1]) 678 { 679 case XEMBED_EMBEDDED_NOTIFY: 680 break; 681 case XEMBED_WINDOW_ACTIVATE: 682 #ifdef DEBUG_XTBIN 683 printf("Xt client get XEMBED_WINDOW_ACTIVATE\n"); 684 #endif 685 break; 686 case XEMBED_WINDOW_DEACTIVATE: 687 #ifdef DEBUG_XTBIN 688 printf("Xt client get XEMBED_WINDOW_DEACTIVATE\n"); 689 #endif 690 break; 691 case XEMBED_MODALITY_ON: 692 #ifdef DEBUG_XTBIN 693 printf("Xt client get XEMBED_MODALITY_ON\n"); 694 #endif 695 break; 696 case XEMBED_MODALITY_OFF: 697 #ifdef DEBUG_XTBIN 698 printf("Xt client get XEMBED_MODALITY_OFF\n"); 699 #endif 700 break; 701 case XEMBED_FOCUS_IN: 702 case XEMBED_FOCUS_OUT: 703 { 704 XEvent xevent; 705 memset(&xevent, 0, sizeof(xevent)); 706 707 if(event->xclient.data.l[1] == XEMBED_FOCUS_IN) { 708 #ifdef DEBUG_XTBIN 709 printf("XTEMBED got focus in\n"); 710 #endif 711 xevent.xfocus.type = FocusIn; 712 } 713 else { 714 #ifdef DEBUG_XTBIN 715 printf("XTEMBED got focus out\n"); 716 #endif 717 xevent.xfocus.type = FocusOut; 718 } 719 720 xevent.xfocus.window = XtWindow(xtplug->child_widget); 721 xevent.xfocus.display = XtDisplay(xtplug->child_widget); 722 XSendEvent(XtDisplay(xtplug->child_widget), 723 xevent.xfocus.window, 724 False, NoEventMask, 725 &xevent ); 726 XSync( XtDisplay(xtplug->child_widget), False); 727 } 728 break; 729 default: 730 break; 731 } /* End of XEmbed Message */ 732 } 733 734 static void 735 xt_client_event_handler( Widget w, XtPointer client_data, XEvent *event) 736 { 737 XtClient *xtplug = (XtClient*)client_data; 738 739 switch(event->type) 740 { 741 case ClientMessage: 742 /* Handle xembed message */ 743 if (event->xclient.message_type== 744 XInternAtom (XtDisplay(xtplug->child_widget), 745 "_XEMBED", False)) { 746 xt_client_handle_xembed_message(w, client_data, event); 747 } 748 break; 749 case ReparentNotify: 750 break; 751 case MappingNotify: 752 xt_client_set_info (w, XEMBED_MAPPED); 753 break; 754 case UnmapNotify: 755 xt_client_set_info (w, 0); 756 break; 757 case FocusIn: 758 send_xembed_message ( xtplug, 759 XEMBED_REQUEST_FOCUS, 0, 0, 0, 0); 760 break; 761 case FocusOut: 762 break; 763 case KeyPress: 764 #ifdef DEBUG_XTBIN 765 printf("Key Press Got!\n"); 766 #endif 767 break; 768 default: 769 break; 770 } /* End of switch(event->type) */ 771 } 772 773 static void 774 send_xembed_message (XtClient *xtclient, 775 long message, 776 long detail, 777 long data1, 778 long data2, 779 long time) 780 { 781 XEvent xevent; 782 Window w=XtWindow(xtclient->top_widget); 783 Display* dpy=xtclient->xtdisplay; 784 int errorcode; 785 786 memset(&xevent,0,sizeof(xevent)); 787 xevent.xclient.window = w; 788 xevent.xclient.type = ClientMessage; 789 xevent.xclient.message_type = XInternAtom(dpy,"_XEMBED",False); 790 xevent.xclient.format = 32; 791 xevent.xclient.data.l[0] = time; 792 xevent.xclient.data.l[1] = message; 793 xevent.xclient.data.l[2] = detail; 794 xevent.xclient.data.l[3] = data1; 795 xevent.xclient.data.l[4] = data2; 796 797 trap_errors (); 798 XSendEvent (dpy, w, False, NoEventMask, &xevent); 799 XSync (dpy,False); 800 801 if((errorcode = untrap_error())) { 802 #ifdef DEBUG_XTBIN 803 printf("send_xembed_message error(%d)!!!\n",errorcode); 804 #endif 805 } 806 } 807 808 static int 809 error_handler(Display *display, XErrorEvent *error) 810 { 811 trapped_error_code = error->error_code; 812 return 0; 813 } 814 815 static void 816 trap_errors(void) 817 { 818 trapped_error_code =0; 819 old_error_handler = XSetErrorHandler(error_handler); 820 } 821 822 static int 823 untrap_error(void) 824 { 825 XSetErrorHandler(old_error_handler); 826 if(trapped_error_code) { 827 #ifdef DEBUG_XTBIN 828 printf("Get X Window Error = %d\n", trapped_error_code); 829 #endif 830 } 831 return trapped_error_code; 832 } 833 834 static void 835 xt_client_focus_listener( Widget w, XtPointer user_data, XEvent *event) 836 { 837 Display *dpy = XtDisplay(w); 838 XtClient *xtclient = user_data; 839 Window win = XtWindow(w); 840 841 switch(event->type) 842 { 843 case CreateNotify: 844 if(event->xcreatewindow.parent == win) { 845 Widget child=XtWindowToWidget( dpy, event->xcreatewindow.window); 846 if (child) 847 xt_add_focus_listener_tree(child, user_data); 848 } 849 break; 850 case DestroyNotify: 851 xt_remove_focus_listener( w, user_data); 852 break; 853 case ReparentNotify: 854 if(event->xreparent.parent == win) { 855 /* I am the new parent */ 856 Widget child=XtWindowToWidget(dpy, event->xreparent.window); 857 if (child) 858 xt_add_focus_listener_tree( child, user_data); 859 } 860 else if(event->xreparent.window == win) { 861 /* I am the new child */ 862 } 863 else { 864 /* I am the old parent */ 865 } 866 break; 867 case ButtonRelease: 868 #if 0 869 XSetInputFocus(dpy, XtWindow(xtclient->child_widget), RevertToParent, event->xbutton.time); 870 #endif 871 send_xembed_message ( xtclient, 872 XEMBED_REQUEST_FOCUS, 0, 0, 0, 0); 873 break; 874 default: 875 break; 876 } /* End of switch(event->type) */ 877 } 878 879 static void 880 xt_add_focus_listener( Widget w, XtPointer user_data) 881 { 882 XWindowAttributes attr; 883 long eventmask; 884 XtClient *xtclient = user_data; 885 int errorcode; 886 887 trap_errors (); 888 XGetWindowAttributes(XtDisplay(w), XtWindow(w), &attr); 889 eventmask = attr.your_event_mask | SubstructureNotifyMask | ButtonReleaseMask; 890 XSelectInput(XtDisplay(w), 891 XtWindow(w), 892 eventmask); 893 894 XtAddEventHandler(w, 895 SubstructureNotifyMask | ButtonReleaseMask, 896 TRUE, 897 (XtEventHandler)xt_client_focus_listener, 898 xtclient); 899 untrap_error(); 900 } 901 902 static void 903 xt_remove_focus_listener(Widget w, XtPointer user_data) 904 { 905 int errorcode; 906 907 trap_errors (); 908 XtRemoveEventHandler(w, SubstructureNotifyMask | ButtonReleaseMask, TRUE, 909 (XtEventHandler)xt_client_focus_listener, user_data); 910 911 untrap_error(); 912 } 913 914 static void 915 xt_add_focus_listener_tree ( Widget treeroot, XtPointer user_data) 916 { 917 Window win = XtWindow(treeroot); 918 Window *children; 919 Window root, parent; 920 Display *dpy = XtDisplay(treeroot); 921 unsigned int i, nchildren; 922 923 /* ensure we don't add more than once */ 924 xt_remove_focus_listener( treeroot, user_data); 925 xt_add_focus_listener( treeroot, user_data); 926 trap_errors(); 927 if(!XQueryTree(dpy, win, &root, &parent, &children, &nchildren)) { 928 untrap_error(); 929 return; 930 } 931 932 if(untrap_error()) 933 return; 934 935 for(i=0; i<nchildren; ++i) { 936 Widget child = XtWindowToWidget(dpy, children[i]); 937 if (child) 938 xt_add_focus_listener_tree( child, user_data); 939 } 940 XFree((void*)children); 941 942 return; 943 } 944