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