#undef GTK_DISABLE_DEPRECATED
#include "xembed.h"
#include "gtk2xtbin.h"
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <glib.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Shell.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#undef DEBUG_XTBIN
#define XTBIN_MAX_EVENTS 30
static void gtk_xtbin_class_init (GtkXtBinClass *klass);
static void gtk_xtbin_init (GtkXtBin *xtbin);
static void gtk_xtbin_realize (GtkWidget *widget);
static void gtk_xtbin_unrealize (GtkWidget *widget);
static void gtk_xtbin_destroy (GtkObject *object);
static void gtk_xtbin_shutdown (GtkObject *object);
static void xt_client_init (XtClient * xtclient,
Visual *xtvisual,
Colormap xtcolormap,
int xtdepth);
static void xt_client_create (XtClient * xtclient,
Window embeder,
int height,
int width );
static void xt_client_unrealize (XtClient* xtclient);
static void xt_client_destroy (XtClient* xtclient);
static void xt_client_set_info (Widget xtplug,
unsigned long flags);
static void xt_client_event_handler (Widget w,
XtPointer client_data,
XEvent *event);
static void xt_client_handle_xembed_message (Widget w,
XtPointer client_data,
XEvent *event);
static void xt_client_focus_listener (Widget w,
XtPointer user_data,
XEvent *event);
static void xt_add_focus_listener( Widget w, XtPointer user_data );
static void xt_add_focus_listener_tree ( Widget treeroot, XtPointer user_data);
static void xt_remove_focus_listener(Widget w, XtPointer user_data);
static void send_xembed_message (XtClient *xtclient,
long message,
long detail,
long data1,
long data2,
long time);
static int error_handler (Display *display,
XErrorEvent *error);
static void trap_errors(void);
static int untrap_error(void);
static int (*old_error_handler) (Display *, XErrorEvent *);
static int trapped_error_code = 0;
static GtkWidgetClass *parent_class = NULL;
static Display *xtdisplay = NULL;
static String *fallback = NULL;
static gboolean xt_is_initialized = FALSE;
static gint num_widgets = 0;
static GPollFD xt_event_poll_fd;
static gint xt_polling_timer_id = 0;
static guint tag = 0;
static gboolean
xt_event_prepare (GSource* source_data,
gint *timeout)
{
int mask;
GDK_THREADS_ENTER();
mask = XPending(xtdisplay);
GDK_THREADS_LEAVE();
return (gboolean)mask;
}
static gboolean
xt_event_check (GSource* source_data)
{
GDK_THREADS_ENTER ();
if (xt_event_poll_fd.revents & G_IO_IN) {
int mask;
mask = XPending(xtdisplay);
GDK_THREADS_LEAVE ();
return (gboolean)mask;
}
GDK_THREADS_LEAVE ();
return FALSE;
}
static gboolean
xt_event_dispatch (GSource* source_data,
GSourceFunc call_back,
gpointer user_data)
{
XEvent event;
XtAppContext ac;
int i = 0;
ac = XtDisplayToApplicationContext(xtdisplay);
GDK_THREADS_ENTER ();
for (i=0; i < XTBIN_MAX_EVENTS && XPending(xtdisplay); i++) {
XtAppProcessEvent(ac, XtIMXEvent);
}
GDK_THREADS_LEAVE ();
return TRUE;
}
static GSourceFuncs xt_event_funcs = {
xt_event_prepare,
xt_event_check,
xt_event_dispatch,
g_free,
(GSourceFunc)NULL,
(GSourceDummyMarshal)NULL
};
static gboolean
xt_event_polling_timer_callback(gpointer user_data)
{
Display * display;
XtAppContext ac;
int eventsToProcess = 20;
display = (Display *)user_data;
ac = XtDisplayToApplicationContext(display);
while (eventsToProcess-- && XtAppPending(ac))
XtAppProcessEvent(ac, XtIMAll);
return TRUE;
}
GType
gtk_xtbin_get_type (void)
{
static GType xtbin_type = 0;
if (!xtbin_type) {
static const GTypeInfo xtbin_info =
{
sizeof (GtkXtBinClass),
NULL,
NULL,
(GClassInitFunc)gtk_xtbin_class_init,
NULL,
NULL,
sizeof (GtkXtBin),
0,
(GInstanceInitFunc)gtk_xtbin_init,
};
xtbin_type = g_type_register_static (GTK_TYPE_SOCKET,
"GtkXtBin",
&xtbin_info,
0);
}
return xtbin_type;
}
static void
gtk_xtbin_class_init (GtkXtBinClass *klass)
{
GtkWidgetClass *widget_class;
GtkObjectClass *object_class;
parent_class = gtk_type_class (GTK_TYPE_SOCKET);
widget_class = GTK_WIDGET_CLASS (klass);
widget_class->realize = gtk_xtbin_realize;
widget_class->unrealize = gtk_xtbin_unrealize;
object_class = GTK_OBJECT_CLASS (klass);
object_class->destroy = gtk_xtbin_destroy;
}
static void
gtk_xtbin_init (GtkXtBin *xtbin)
{
xtbin->xtdisplay = NULL;
xtbin->parent_window = NULL;
xtbin->xtwindow = 0;
xtbin->x = 0;
xtbin->y = 0;
}
static void
gtk_xtbin_realize (GtkWidget *widget)
{
GtkXtBin *xtbin;
GtkAllocation allocation = { 0, 0, 200, 200 };
gint x, y, w, h, d;
#ifdef DEBUG_XTBIN
printf("gtk_xtbin_realize()\n");
#endif
g_return_if_fail (GTK_IS_XTBIN (widget));
xtbin = GTK_XTBIN (widget);
gdk_window_get_geometry(xtbin->parent_window, &x, &y, &w, &h, &d);
allocation.width = w;
allocation.height = h;
gtk_widget_size_allocate (widget, &allocation);
#ifdef DEBUG_XTBIN
printf("initial allocation %d %d %d %d\n", x, y, w, h);
#endif
xtbin->width = widget->allocation.width;
xtbin->height = widget->allocation.height;
(*GTK_WIDGET_CLASS(parent_class)->realize)(widget);
xt_client_create(&(xtbin->xtclient),
gtk_socket_get_id(GTK_SOCKET(xtbin)),
xtbin->height,
xtbin->width);
xtbin->xtwindow = XtWindow(xtbin->xtclient.child_widget);
gdk_flush();
gtk_socket_add_id(GTK_SOCKET(widget), xtbin->xtwindow);
}
GtkWidget*
gtk_xtbin_new (GdkWindow *parent_window, String * f)
{
GtkXtBin *xtbin;
gpointer user_data;
assert(parent_window != NULL);
xtbin = gtk_type_new (GTK_TYPE_XTBIN);
if (!xtbin)
return (GtkWidget*)NULL;
if (f)
fallback = f;
xtbin->parent_window = parent_window;
xt_client_init(&(xtbin->xtclient),
GDK_VISUAL_XVISUAL(gdk_rgb_get_visual()),
GDK_COLORMAP_XCOLORMAP(gdk_rgb_get_colormap()),
gdk_rgb_get_visual()->depth);
if (!xtbin->xtclient.xtdisplay) {
#ifdef DEBUG_XTBIN
printf("gtk_xtbin_init: XtOpenDisplay() returned NULL.\n");
#endif
g_free (xtbin);
return (GtkWidget *)NULL;
}
if (0 == num_widgets) {
int cnumber;
GSource* gs = g_source_new(&xt_event_funcs, sizeof(GSource));
if (!gs) {
return NULL;
}
g_source_set_priority(gs, GDK_PRIORITY_EVENTS);
g_source_set_can_recurse(gs, TRUE);
tag = g_source_attach(gs, (GMainContext*)NULL);
#ifdef VMS
cnumber = XConnectionNumber(xtdisplay);
#else
cnumber = ConnectionNumber(xtdisplay);
#endif
xt_event_poll_fd.fd = cnumber;
xt_event_poll_fd.events = G_IO_IN;
xt_event_poll_fd.revents = 0;
g_main_context_add_poll ((GMainContext*)NULL,
&xt_event_poll_fd,
G_PRIORITY_LOW);
xt_polling_timer_id =
gtk_timeout_add(25,
(GtkFunction)xt_event_polling_timer_callback,
xtdisplay);
}
num_widgets++;
xtbin->xtdisplay = xtbin->xtclient.xtdisplay;
gtk_widget_set_parent_window(GTK_WIDGET(xtbin), parent_window);
gdk_window_get_user_data(xtbin->parent_window, &user_data);
if (user_data)
gtk_container_add(GTK_CONTAINER(user_data), GTK_WIDGET(xtbin));
return GTK_WIDGET (xtbin);
}
void
gtk_xtbin_set_position (GtkXtBin *xtbin,
gint x,
gint y)
{
xtbin->x = x;
xtbin->y = y;
if (GTK_WIDGET_REALIZED (xtbin))
gdk_window_move (GTK_WIDGET (xtbin)->window, x, y);
}
void
gtk_xtbin_resize (GtkWidget *widget,
gint width,
gint height)
{
Arg args[2];
GtkXtBin *xtbin = GTK_XTBIN (widget);
GtkAllocation allocation;
#ifdef DEBUG_XTBIN
printf("gtk_xtbin_resize %p %d %d\n", (void *)widget, width, height);
#endif
xtbin->height = height;
xtbin->width = width;
if (height <= 0 || width <=0) {
height = 1;
width = 1;
}
XtSetArg(args[0], XtNheight, height);
XtSetArg(args[1], XtNwidth, width);
XtSetValues(xtbin->xtclient.top_widget, args, 2);
allocation.x = xtbin->x;
allocation.y = xtbin->y;
allocation.width = xtbin->width;
allocation.height = xtbin->height;
gtk_widget_size_allocate(widget, &allocation);
}
static void
gtk_xtbin_unrealize (GtkWidget *object)
{
GtkXtBin *xtbin;
GtkWidget *widget;
#ifdef DEBUG_XTBIN
printf("gtk_xtbin_unrealize()\n");
#endif
xtbin = GTK_XTBIN(object);
widget = GTK_WIDGET(object);
GTK_WIDGET_UNSET_FLAGS (widget, GTK_VISIBLE);
if (GTK_WIDGET_REALIZED (widget)) {
xt_client_unrealize(&(xtbin->xtclient));
}
(*GTK_WIDGET_CLASS (parent_class)->unrealize)(widget);
}
static void
gtk_xtbin_destroy (GtkObject *object)
{
GtkXtBin *xtbin;
#ifdef DEBUG_XTBIN
printf("gtk_xtbin_destroy()\n");
#endif
g_return_if_fail (object != NULL);
g_return_if_fail (GTK_IS_XTBIN (object));
xtbin = GTK_XTBIN (object);
if(xtbin->xtwindow) {
xt_client_destroy(&(xtbin->xtclient));
xtbin->xtwindow = 0;
num_widgets--;
if (0 == num_widgets) {
#ifdef DEBUG_XTBIN
printf("removing the Xt connection from the main loop\n");
#endif
g_main_context_remove_poll((GMainContext*)NULL, &xt_event_poll_fd);
g_source_remove(tag);
gtk_timeout_remove(xt_polling_timer_id);
xt_polling_timer_id = 0;
}
}
GTK_OBJECT_CLASS(parent_class)->destroy(object);
}
static void
xt_client_init( XtClient * xtclient,
Visual *xtvisual,
Colormap xtcolormap,
int xtdepth)
{
XtAppContext app_context;
char *mArgv[1];
int mArgc = 0;
xtclient->top_widget = NULL;
xtclient->child_widget = NULL;
xtclient->xtdisplay = NULL;
xtclient->xtvisual = NULL;
xtclient->xtcolormap = 0;
xtclient->xtdepth = 0;
if (!xt_is_initialized) {
#ifdef DEBUG_XTBIN
printf("starting up Xt stuff\n");
#endif
XtToolkitInitialize();
app_context = XtCreateApplicationContext();
if (fallback)
XtAppSetFallbackResources(app_context, fallback);
xtdisplay = XtOpenDisplay(app_context, gdk_get_display(), NULL,
"Wrapper", NULL, 0, &mArgc, mArgv);
if (xtdisplay)
xt_is_initialized = TRUE;
}
xtclient->xtdisplay = xtdisplay;
xtclient->xtvisual = xtvisual;
xtclient->xtcolormap = xtcolormap;
xtclient->xtdepth = xtdepth;
}
static void
xt_client_create ( XtClient* xtclient ,
Window embedderid,
int height,
int width )
{
int n;
Arg args[6];
Widget child_widget;
Widget top_widget;
#ifdef DEBUG_XTBIN
printf("xt_client_create() \n");
#endif
top_widget = XtAppCreateShell("drawingArea", "Wrapper",
applicationShellWidgetClass,
xtclient->xtdisplay,
NULL, 0);
xtclient->top_widget = top_widget;
n = 0;
XtSetArg(args[n], XtNheight, height);n++;
XtSetArg(args[n], XtNwidth, width);n++;
XtSetValues(top_widget, args, n);
child_widget = XtVaCreateWidget("form",
compositeWidgetClass,
top_widget, NULL);
n = 0;
XtSetArg(args[n], XtNheight, height);n++;
XtSetArg(args[n], XtNwidth, width);n++;
XtSetArg(args[n], XtNvisual, xtclient->xtvisual ); n++;
XtSetArg(args[n], XtNdepth, xtclient->xtdepth ); n++;
XtSetArg(args[n], XtNcolormap, xtclient->xtcolormap ); n++;
XtSetArg(args[n], XtNborderWidth, 0); n++;
XtSetValues(child_widget, args, n);
XSync(xtclient->xtdisplay, FALSE);
xtclient->oldwindow = top_widget->core.window;
top_widget->core.window = embedderid;
#if XlibSpecificationRelease >= 6
XtRegisterDrawable(xtclient->xtdisplay,
embedderid,
top_widget);
#else
_XtRegisterWindow( embedderid,
top_widget);
#endif
XtRealizeWidget(child_widget);
XSelectInput(xtclient->xtdisplay,
XtWindow(top_widget),
0x0FFFFF);
xt_client_set_info (child_widget, 0);
XtManageChild(child_widget);
xtclient->child_widget = child_widget;
XtAddEventHandler(child_widget,
0x0FFFFF & ~ResizeRedirectMask,
TRUE,
(XtEventHandler)xt_client_event_handler, xtclient);
XtAddEventHandler(child_widget,
SubstructureNotifyMask | ButtonReleaseMask,
TRUE,
(XtEventHandler)xt_client_focus_listener,
xtclient);
XSync(xtclient->xtdisplay, FALSE);
}
static void
xt_client_unrealize ( XtClient* xtclient )
{
#if XlibSpecificationRelease >= 6
XtUnregisterDrawable(xtclient->xtdisplay,
xtclient->top_widget->core.window);
#else
_XtUnregisterWindow(xtclient->top_widget->core.window,
xtclient->top_widget);
#endif
XSync(xtclient->xtdisplay, False);
xtclient->top_widget->core.window = xtclient->oldwindow;
XtUnrealizeWidget(xtclient->top_widget);
}
static void
xt_client_destroy (XtClient* xtclient)
{
if(xtclient->top_widget) {
XtRemoveEventHandler(xtclient->child_widget, 0x0FFFFF, TRUE,
(XtEventHandler)xt_client_event_handler, xtclient);
XtDestroyWidget(xtclient->top_widget);
xtclient->top_widget = NULL;
}
}
static void
xt_client_set_info (Widget xtplug, unsigned long flags)
{
unsigned long buffer[2];
Atom infoAtom = XInternAtom(XtDisplay(xtplug), "_XEMBED_INFO", False);
buffer[1] = 0;
buffer[1] = flags;
XChangeProperty (XtDisplay(xtplug), XtWindow(xtplug),
infoAtom, infoAtom, 32,
PropModeReplace,
(unsigned char *)buffer, 2);
}
static void
xt_client_handle_xembed_message(Widget w, XtPointer client_data, XEvent *event)
{
XtClient *xtplug = (XtClient*)client_data;
switch (event->xclient.data.l[1])
{
case XEMBED_EMBEDDED_NOTIFY:
break;
case XEMBED_WINDOW_ACTIVATE:
#ifdef DEBUG_XTBIN
printf("Xt client get XEMBED_WINDOW_ACTIVATE\n");
#endif
break;
case XEMBED_WINDOW_DEACTIVATE:
#ifdef DEBUG_XTBIN
printf("Xt client get XEMBED_WINDOW_DEACTIVATE\n");
#endif
break;
case XEMBED_MODALITY_ON:
#ifdef DEBUG_XTBIN
printf("Xt client get XEMBED_MODALITY_ON\n");
#endif
break;
case XEMBED_MODALITY_OFF:
#ifdef DEBUG_XTBIN
printf("Xt client get XEMBED_MODALITY_OFF\n");
#endif
break;
case XEMBED_FOCUS_IN:
case XEMBED_FOCUS_OUT:
{
XEvent xevent;
memset(&xevent, 0, sizeof(xevent));
if(event->xclient.data.l[1] == XEMBED_FOCUS_IN) {
#ifdef DEBUG_XTBIN
printf("XTEMBED got focus in\n");
#endif
xevent.xfocus.type = FocusIn;
}
else {
#ifdef DEBUG_XTBIN
printf("XTEMBED got focus out\n");
#endif
xevent.xfocus.type = FocusOut;
}
xevent.xfocus.window = XtWindow(xtplug->child_widget);
xevent.xfocus.display = XtDisplay(xtplug->child_widget);
XSendEvent(XtDisplay(xtplug->child_widget),
xevent.xfocus.window,
False, NoEventMask,
&xevent );
XSync( XtDisplay(xtplug->child_widget), False);
}
break;
default:
break;
}
}
static void
xt_client_event_handler( Widget w, XtPointer client_data, XEvent *event)
{
XtClient *xtplug = (XtClient*)client_data;
switch(event->type)
{
case ClientMessage:
if (event->xclient.message_type==
XInternAtom (XtDisplay(xtplug->child_widget),
"_XEMBED", False)) {
xt_client_handle_xembed_message(w, client_data, event);
}
break;
case ReparentNotify:
break;
case MappingNotify:
xt_client_set_info (w, XEMBED_MAPPED);
break;
case UnmapNotify:
xt_client_set_info (w, 0);
break;
case FocusIn:
send_xembed_message ( xtplug,
XEMBED_REQUEST_FOCUS, 0, 0, 0, 0);
break;
case FocusOut:
break;
case KeyPress:
#ifdef DEBUG_XTBIN
printf("Key Press Got!\n");
#endif
break;
default:
break;
}
}
static void
send_xembed_message (XtClient *xtclient,
long message,
long detail,
long data1,
long data2,
long time)
{
XEvent xevent;
Window w=XtWindow(xtclient->top_widget);
Display* dpy=xtclient->xtdisplay;
int errorcode;
memset(&xevent,0,sizeof(xevent));
xevent.xclient.window = w;
xevent.xclient.type = ClientMessage;
xevent.xclient.message_type = XInternAtom(dpy,"_XEMBED",False);
xevent.xclient.format = 32;
xevent.xclient.data.l[0] = time;
xevent.xclient.data.l[1] = message;
xevent.xclient.data.l[2] = detail;
xevent.xclient.data.l[3] = data1;
xevent.xclient.data.l[4] = data2;
trap_errors ();
XSendEvent (dpy, w, False, NoEventMask, &xevent);
XSync (dpy,False);
if((errorcode = untrap_error())) {
#ifdef DEBUG_XTBIN
printf("send_xembed_message error(%d)!!!\n",errorcode);
#endif
}
}
static int
error_handler(Display *display, XErrorEvent *error)
{
trapped_error_code = error->error_code;
return 0;
}
static void
trap_errors(void)
{
trapped_error_code =0;
old_error_handler = XSetErrorHandler(error_handler);
}
static int
untrap_error(void)
{
XSetErrorHandler(old_error_handler);
if(trapped_error_code) {
#ifdef DEBUG_XTBIN
printf("Get X Window Error = %d\n", trapped_error_code);
#endif
}
return trapped_error_code;
}
static void
xt_client_focus_listener( Widget w, XtPointer user_data, XEvent *event)
{
Display *dpy = XtDisplay(w);
XtClient *xtclient = user_data;
Window win = XtWindow(w);
switch(event->type)
{
case CreateNotify:
if(event->xcreatewindow.parent == win) {
Widget child=XtWindowToWidget( dpy, event->xcreatewindow.window);
if (child)
xt_add_focus_listener_tree(child, user_data);
}
break;
case DestroyNotify:
xt_remove_focus_listener( w, user_data);
break;
case ReparentNotify:
if(event->xreparent.parent == win) {
Widget child=XtWindowToWidget(dpy, event->xreparent.window);
if (child)
xt_add_focus_listener_tree( child, user_data);
}
else if(event->xreparent.window == win) {
}
else {
}
break;
case ButtonRelease:
#if 0
XSetInputFocus(dpy, XtWindow(xtclient->child_widget), RevertToParent, event->xbutton.time);
#endif
send_xembed_message ( xtclient,
XEMBED_REQUEST_FOCUS, 0, 0, 0, 0);
break;
default:
break;
}
}
static void
xt_add_focus_listener( Widget w, XtPointer user_data)
{
XWindowAttributes attr;
long eventmask;
XtClient *xtclient = user_data;
int errorcode;
trap_errors ();
XGetWindowAttributes(XtDisplay(w), XtWindow(w), &attr);
eventmask = attr.your_event_mask | SubstructureNotifyMask | ButtonReleaseMask;
XSelectInput(XtDisplay(w),
XtWindow(w),
eventmask);
XtAddEventHandler(w,
SubstructureNotifyMask | ButtonReleaseMask,
TRUE,
(XtEventHandler)xt_client_focus_listener,
xtclient);
untrap_error();
}
static void
xt_remove_focus_listener(Widget w, XtPointer user_data)
{
int errorcode;
trap_errors ();
XtRemoveEventHandler(w, SubstructureNotifyMask | ButtonReleaseMask, TRUE,
(XtEventHandler)xt_client_focus_listener, user_data);
untrap_error();
}
static void
xt_add_focus_listener_tree ( Widget treeroot, XtPointer user_data)
{
Window win = XtWindow(treeroot);
Window *children;
Window root, parent;
Display *dpy = XtDisplay(treeroot);
unsigned int i, nchildren;
xt_remove_focus_listener( treeroot, user_data);
xt_add_focus_listener( treeroot, user_data);
trap_errors();
if(!XQueryTree(dpy, win, &root, &parent, &children, &nchildren)) {
untrap_error();
return;
}
if(untrap_error())
return;
for(i=0; i<nchildren; ++i) {
Widget child = XtWindowToWidget(dpy, children[i]);
if (child)
xt_add_focus_listener_tree( child, user_data);
}
XFree((void*)children);
return;
}