106 applicationActive = toplevel.isFocused();
107 }
108 }
109
110 void deinstallActivateListener() {
111 Window toplevel = getTopLevel(target);
112 if (toplevel != null) {
113 toplevel.removeWindowFocusListener(this);
114 }
115 }
116
117 boolean isXEmbedActive() {
118 return xembed.handle != 0;
119 }
120
121 boolean isApplicationActive() {
122 return applicationActive;
123 }
124
125 void initDispatching() {
126 if (xembedLog.isLoggable(PlatformLogger.FINE)) {
127 xembedLog.fine("Init embedding for " + Long.toHexString(xembed.handle));
128 }
129 XToolkit.awtLock();
130 try {
131 XToolkit.addEventDispatcher(xembed.handle, xembed);
132 XlibWrapper.XSelectInput(XToolkit.getDisplay(), xembed.handle,
133 XConstants.StructureNotifyMask | XConstants.PropertyChangeMask);
134
135 XDropTargetRegistry.getRegistry().registerXEmbedClient(getWindow(), xembed.handle);
136 } finally {
137 XToolkit.awtUnlock();
138 }
139 xembed.processXEmbedInfo();
140
141 notifyChildEmbedded();
142 }
143
144 void endDispatching() {
145 if (xembedLog.isLoggable(PlatformLogger.FINE)) {
146 xembedLog.fine("End dispatching for " + Long.toHexString(xembed.handle));
147 }
148 XToolkit.awtLock();
149 try {
150 XDropTargetRegistry.getRegistry().unregisterXEmbedClient(getWindow(), xembed.handle);
151 // We can't deselect input since someone else might be interested in it
152 XToolkit.removeEventDispatcher(xembed.handle, xembed);
153 } finally {
154 XToolkit.awtUnlock();
155 }
156 }
157
158 void embedChild(long child) {
159 if (xembed.handle != 0) {
160 detachChild();
161 }
162 xembed.handle = child;
163 initDispatching();
164 }
165
166 void childDestroyed() {
167 if (xembedLog.isLoggable(PlatformLogger.FINE)) {
168 xembedLog.fine("Child " + Long.toHexString(xembed.handle) + " has self-destroyed.");
169 }
170 endDispatching();
171 xembed.handle = 0;
172 }
173
174 public void handleEvent(AWTEvent e) {
175 super.handleEvent(e);
176 if (isXEmbedActive()) {
177 switch (e.getID()) {
178 case FocusEvent.FOCUS_GAINED:
179 canvasFocusGained((FocusEvent)e);
180 break;
181 case FocusEvent.FOCUS_LOST:
182 canvasFocusLost((FocusEvent)e);
183 break;
184 case KeyEvent.KEY_PRESSED:
185 case KeyEvent.KEY_RELEASED:
186 if (!((InputEvent)e).isConsumed()) {
187 forwardKeyEvent((KeyEvent)e);
188 }
189 break;
190 }
191 }
192 }
193
194 public void dispatchEvent(XEvent ev) {
195 super.dispatchEvent(ev);
196 switch (ev.get_type()) {
197 case XConstants.CreateNotify:
198 XCreateWindowEvent cr = ev.get_xcreatewindow();
199 if (xembedLog.isLoggable(PlatformLogger.FINEST)) {
200 xembedLog.finest("Message on embedder: " + cr);
201 }
202 if (xembedLog.isLoggable(PlatformLogger.FINER)) {
203 xembedLog.finer("Create notify for parent " + Long.toHexString(cr.get_parent()) +
204 ", window " + Long.toHexString(cr.get_window()));
205 }
206 embedChild(cr.get_window());
207 break;
208 case XConstants.DestroyNotify:
209 XDestroyWindowEvent dn = ev.get_xdestroywindow();
210 if (xembedLog.isLoggable(PlatformLogger.FINEST)) {
211 xembedLog.finest("Message on embedder: " + dn);
212 }
213 if (xembedLog.isLoggable(PlatformLogger.FINER)) {
214 xembedLog.finer("Destroy notify for parent: " + dn);
215 }
216 childDestroyed();
217 break;
218 case XConstants.ReparentNotify:
219 XReparentEvent rep = ev.get_xreparent();
220 if (xembedLog.isLoggable(PlatformLogger.FINEST)) {
221 xembedLog.finest("Message on embedder: " + rep);
222 }
223 if (xembedLog.isLoggable(PlatformLogger.FINER)) {
224 xembedLog.finer("Reparent notify for parent " + Long.toHexString(rep.get_parent()) +
225 ", window " + Long.toHexString(rep.get_window()) +
226 ", event " + Long.toHexString(rep.get_event()));
227 }
228 if (rep.get_parent() == getWindow()) {
229 // Reparented into us - embed it
230 embedChild(rep.get_window());
231 } else {
232 // Reparented out of us - detach it
233 childDestroyed();
234 }
235 break;
236 }
237 }
238
239 public Dimension getPreferredSize() {
240 if (isXEmbedActive()) {
241 XToolkit.awtLock();
242 try {
243 long p_hints = XlibWrapper.XAllocSizeHints();
306 xembed.handle, wattr.pData);
307
308 XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
309
310 if ((status == 0) ||
311 ((XErrorHandlerUtil.saved_error != null) &&
312 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success))) {
313 return null;
314 }
315
316 return new Rectangle(wattr.get_x(), wattr.get_y(), wattr.get_width(), wattr.get_height());
317 } finally {
318 wattr.dispose();
319 }
320 } finally {
321 XToolkit.awtUnlock();
322 }
323 }
324
325 void childResized() {
326 if (xembedLog.isLoggable(PlatformLogger.FINER)) {
327 Rectangle bounds = getClientBounds();
328 xembedLog.finer("Child resized: " + bounds);
329 // It is not required to update embedder's size when client size changes
330 // However, since there is no any means to get client size it seems to be the
331 // only way to provide it. However, it contradicts with Java layout concept -
332 // so it is disabled for now.
333 // Rectangle my_bounds = getBounds();
334 // setBounds(my_bounds.x, my_bounds.y, bounds.width, bounds.height, SET_BOUNDS);
335 }
336 XToolkit.postEvent(XToolkit.targetToAppContext(target), new ComponentEvent(target, ComponentEvent.COMPONENT_RESIZED));
337 }
338
339 void focusNext() {
340 if (isXEmbedActive()) {
341 xembedLog.fine("Requesting focus for the next component after embedder");
342 postEvent(new InvocationEvent(target, new Runnable() {
343 public void run() {
344 KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent(target);
345 }
346 }));
371 }
372 }));
373 } else {
374 xembedLog.fine("XEmbed is not active - denying request focus");
375 }
376 }
377
378 void notifyChildEmbedded() {
379 xembed.sendMessage(xembed.handle, XEMBED_EMBEDDED_NOTIFY, getWindow(), Math.min(xembed.version, XEMBED_VERSION), 0);
380 if (isApplicationActive()) {
381 xembedLog.fine("Sending WINDOW_ACTIVATE during initialization");
382 xembed.sendMessage(xembed.handle, XEMBED_WINDOW_ACTIVATE);
383 if (hasFocus()) {
384 xembedLog.fine("Sending FOCUS_GAINED during initialization");
385 xembed.sendMessage(xembed.handle, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0);
386 }
387 }
388 }
389
390 void detachChild() {
391 if (xembedLog.isLoggable(PlatformLogger.FINE)) {
392 xembedLog.fine("Detaching child " + Long.toHexString(xembed.handle));
393 }
394 /**
395 * XEmbed specification:
396 * "The embedder can unmap the client and reparent the client window to the root window. If the
397 * client receives an ReparentNotify event, it should check the parent field of the XReparentEvent
398 * structure. If this is the root window of the window's screen, then the protocol is finished and
399 * there is no further interaction. If it is a window other than the root window, then the protocol
400 * continues with the new parent acting as the embedder window."
401 */
402 XToolkit.awtLock();
403 try {
404 XlibWrapper.XUnmapWindow(XToolkit.getDisplay(), xembed.handle);
405 XlibWrapper.XReparentWindow(XToolkit.getDisplay(), xembed.handle, XToolkit.getDefaultRootWindow(), 0, 0);
406 } finally {
407 XToolkit.awtUnlock();
408 }
409 endDispatching();
410 xembed.handle = 0;
411 }
454 }
455 }
456 xembed.sendMessage(xembed.handle, XEMBED_FOCUS_OUT, num, 0, 0);
457 }
458 }
459
460 static byte[] getBData(KeyEvent e) {
461 return AWTAccessor.getAWTEventAccessor().getBData(e);
462 }
463
464 void forwardKeyEvent(KeyEvent e) {
465 xembedLog.fine("Try to forward key event");
466 byte[] bdata = getBData(e);
467 long data = Native.toData(bdata);
468 if (data == 0) {
469 return;
470 }
471 try {
472 XKeyEvent ke = new XKeyEvent(data);
473 ke.set_window(xembed.handle);
474 if (xembedLog.isLoggable(PlatformLogger.FINE)) {
475 xembedLog.fine("Forwarding native key event: " + ke);
476 }
477 XToolkit.awtLock();
478 try {
479 XlibWrapper.XSendEvent(XToolkit.getDisplay(), xembed.handle, false, XConstants.NoEventMask, data);
480 } finally {
481 XToolkit.awtUnlock();
482 }
483 } finally {
484 XlibWrapper.unsafe.freeMemory(data);
485 }
486 }
487
488
489 /**
490 * Grab/ungrab key functionality is an unofficial API supported by
491 * GTK. Unfortunately, it doesn't support accelerator API, so,
492 * since this is the ONLY shortcut-processing API available, we
493 * must support it. See XEmbed.NON_STANDARD_XEMBED_GTK_*
494 * messages. The format of these messages is as follows:
495 * - request from client:
496 * data[1] = NON_STANDARD_XEMBED_GTK_GRAB_KEY or NON_STANDARD_XEMBED_GTK_UNGRAB_KEY
497 * data[3] = X keysym
498 * data[4] = X modifiers
499 *
500 * - response from server (in case the grabbed key has been pressed):
501 * forwarded XKeyEvent that matches keysym/modifiers pair
502 */
503 void grabKey(final long keysym, final long modifiers) {
504 postEvent(new InvocationEvent(target, new Runnable() {
505 public void run() {
506 GrabbedKey grab = new GrabbedKey(keysym, modifiers);
507 if (xembedLog.isLoggable(PlatformLogger.FINE)) {
508 xembedLog.fine("Grabbing key: " + grab);
509 }
510 synchronized(GRAB_LOCK) {
511 grabbed_keys.add(grab);
512 }
513 }
514 }));
515 }
516
517 void ungrabKey(final long keysym, final long modifiers) {
518 postEvent(new InvocationEvent(target, new Runnable() {
519 public void run() {
520 GrabbedKey grab = new GrabbedKey(keysym, modifiers);
521 if (xembedLog.isLoggable(PlatformLogger.FINE)) {
522 xembedLog.fine("UnGrabbing key: " + grab);
523 }
524 synchronized(GRAB_LOCK) {
525 grabbed_keys.remove(grab);
526 }
527 }
528 }));
529 }
530
531 void registerAccelerator(final long accel_id, final long keysym, final long modifiers) {
532 postEvent(new InvocationEvent(target, new Runnable() {
533 public void run() {
534 AWTKeyStroke stroke = xembed.getKeyStrokeForKeySym(keysym, modifiers);
535 if (stroke != null) {
536 if (xembedLog.isLoggable(PlatformLogger.FINE)) {
537 xembedLog.fine("Registering accelerator " + accel_id + " for " + stroke);
538 }
539 synchronized(ACCEL_LOCK) {
540 accelerators.put(accel_id, stroke);
541 accel_lookup.put(stroke, accel_id);
542 }
543 }
544 propogateRegisterAccelerator(stroke);
545 }
546 }));
547 }
548
549 void unregisterAccelerator(final long accel_id) {
550 postEvent(new InvocationEvent(target, new Runnable() {
551 public void run() {
552 AWTKeyStroke stroke = null;
553 synchronized(ACCEL_LOCK) {
554 stroke = accelerators.get(accel_id);
555 if (stroke != null) {
556 if (xembedLog.isLoggable(PlatformLogger.FINE)) {
557 xembedLog.fine("Unregistering accelerator: " + accel_id);
558 }
559 accelerators.remove(accel_id);
560 accel_lookup.remove(stroke); // FIXME: How about several accelerators with the same stroke?
561 }
562 }
563 propogateUnRegisterAccelerator(stroke);
564 }
565 }));
566 }
567
568 void propogateRegisterAccelerator(AWTKeyStroke stroke) {
569 // Find the top-level and see if it is XEmbed client. If so, ask him to
570 // register the accelerator
571 XWindowPeer parent = getToplevelXWindow();
572 if (parent != null && parent instanceof XEmbeddedFramePeer) {
573 XEmbeddedFramePeer embedded = (XEmbeddedFramePeer)parent;
574 embedded.registerAccelerator(stroke);
575 }
576 }
584 embedded.unregisterAccelerator(stroke);
585 }
586 }
587
588 public boolean postProcessKeyEvent(KeyEvent e) {
589 // Processing events only if we are in the focused window but
590 // we are not focus owner since otherwise we will get
591 // duplicate shortcut events in the client - one is from
592 // activate_accelerator, another from forwarded event
593 // FIXME: This is probably an incompatibility, protocol
594 // doesn't say anything about disable accelerators when client
595 // is focused.
596
597 XWindowPeer parent = getToplevelXWindow();
598 if (parent == null || !((Window)parent.getTarget()).isFocused() || target.isFocusOwner()) {
599 return false;
600 }
601
602 boolean result = false;
603
604 if (xembedLog.isLoggable(PlatformLogger.FINER)) {
605 xembedLog.finer("Post-processing event " + e);
606 }
607
608 // Process ACCELERATORS
609 AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
610 long accel_id = 0;
611 boolean exists = false;
612 synchronized(ACCEL_LOCK) {
613 exists = accel_lookup.containsKey(stroke);
614 if (exists) {
615 accel_id = accel_lookup.get(stroke).longValue();
616 }
617 }
618 if (exists) {
619 if (xembedLog.isLoggable(PlatformLogger.FINE)) {
620 xembedLog.fine("Activating accelerator " + accel_id);
621 }
622 xembed.sendMessage(xembed.handle, XEMBED_ACTIVATE_ACCELERATOR, accel_id, 0, 0); // FIXME: How about overloaded?
623 result = true;
624 }
625
626 // Process Grabs, unofficial GTK feature
627 exists = false;
628 GrabbedKey key = new GrabbedKey(e);
629 synchronized(GRAB_LOCK) {
630 exists = grabbed_keys.contains(key);
631 }
632 if (exists) {
633 if (xembedLog.isLoggable(PlatformLogger.FINE)) {
634 xembedLog.fine("Forwarding grabbed key " + e);
635 }
636 forwardKeyEvent(e);
637 result = true;
638 }
639
640 return result;
641 }
642
643 public void modalityPushed(ModalityEvent ev) {
644 xembed.sendMessage(xembed.handle, XEMBED_MODALITY_ON);
645 }
646
647 public void modalityPopped(ModalityEvent ev) {
648 xembed.sendMessage(xembed.handle, XEMBED_MODALITY_OFF);
649 }
650
651 public void handleClientMessage(XEvent xev) {
652 super.handleClientMessage(xev);
653 XClientMessageEvent msg = xev.get_xclient();
654 if (xembedLog.isLoggable(PlatformLogger.FINER)) {
655 xembedLog.finer("Client message to embedder: " + msg);
656 }
657 if (msg.get_message_type() == xembed.XEmbed.getAtom()) {
658 if (xembedLog.isLoggable(PlatformLogger.FINE)) {
659 xembedLog.fine(xembed.XEmbedMessageToString(msg));
660 }
661 }
662 if (isXEmbedActive()) {
663 switch ((int)msg.get_data(1)) {
664 case XEMBED_REQUEST_FOCUS:
665 requestXEmbedFocus();
666 break;
667 case XEMBED_FOCUS_NEXT:
668 focusNext();
669 break;
670 case XEMBED_FOCUS_PREV:
671 focusPrev();
672 break;
673 case XEMBED_REGISTER_ACCELERATOR:
674 registerAccelerator(msg.get_data(2), msg.get_data(3), msg.get_data(4));
675 break;
676 case XEMBED_UNREGISTER_ACCELERATOR:
677 unregisterAccelerator(msg.get_data(2));
678 break;
706 public void run() {
707 target.setDropTarget(new XEmbedDropTarget());
708 }
709 };
710 SunToolkit.executeOnEventHandlerThread(target, r);
711 }
712
713 public void removeXEmbedDropTarget() {
714 // Unregister a drop site on the top level.
715 Runnable r = new Runnable() {
716 public void run() {
717 if (target.getDropTarget() instanceof XEmbedDropTarget) {
718 target.setDropTarget(null);
719 }
720 }
721 };
722 SunToolkit.executeOnEventHandlerThread(target, r);
723 }
724
725 public boolean processXEmbedDnDEvent(long ctxt, int eventID) {
726 if (xembedLog.isLoggable(PlatformLogger.FINEST)) {
727 xembedLog.finest(" Drop target=" + target.getDropTarget());
728 }
729 if (target.getDropTarget() instanceof XEmbedDropTarget) {
730 AppContext appContext = XToolkit.targetToAppContext(getTarget());
731 XDropTargetContextPeer peer =
732 XDropTargetContextPeer.getPeer(appContext);
733 peer.forwardEventToEmbedded(xembed.handle, ctxt, eventID);
734 return true;
735 } else {
736 return false;
737 }
738 }
739
740 class XEmbedServer extends XEmbedHelper implements XEventDispatcher {
741 long handle; // Handle to XEmbed client
742 long version;
743 long flags;
744
745 boolean processXEmbedInfo() {
746 long xembed_info_data = Native.allocateLongArray(2);
747 try {
748 if (!XEmbedInfo.getAtomData(handle, xembed_info_data, 2)) {
749 // No more XEMBED_INFO? This is not XEmbed client!
750 // Unfortunately this is the initial state of the most clients
751 // FIXME: add 5-state processing
752 //childDestroyed();
753 xembedLog.finer("Unable to get XEMBED_INFO atom data");
754 return false;
755 }
756 version = Native.getCard32(xembed_info_data, 0);
757 flags = Native.getCard32(xembed_info_data, 1);
758 boolean new_mapped = (flags & XEMBED_MAPPED) != 0;
759 boolean currently_mapped = XlibUtil.getWindowMapState(handle) != XConstants.IsUnmapped;
760 if (new_mapped != currently_mapped) {
761 if (xembedLog.isLoggable(PlatformLogger.FINER)) {
762 xembedLog.finer("Mapping state of the client has changed, old state: " + currently_mapped + ", new state: " + new_mapped);
763 }
764 if (new_mapped) {
765 XToolkit.awtLock();
766 try {
767 XlibWrapper.XMapWindow(XToolkit.getDisplay(), handle);
768 } finally {
769 XToolkit.awtUnlock();
770 }
771 } else {
772 XToolkit.awtLock();
773 try {
774 XlibWrapper.XUnmapWindow(XToolkit.getDisplay(), handle);
775 } finally {
776 XToolkit.awtUnlock();
777 }
778 }
779 } else {
780 if (xembedLog.isLoggable(PlatformLogger.FINER)) {
781 xembedLog.finer("Mapping state didn't change, mapped: " + currently_mapped);
782 }
783 }
784 return true;
785 } finally {
786 XlibWrapper.unsafe.freeMemory(xembed_info_data);
787 }
788 }
789
790 public void handlePropertyNotify(XEvent xev) {
791 if (isXEmbedActive()) {
792 XPropertyEvent ev = xev.get_xproperty();
793 if (xembedLog.isLoggable(PlatformLogger.FINER)) {
794 xembedLog.finer("Property change on client: " + ev);
795 }
796 if (ev.get_atom() == XAtom.XA_WM_NORMAL_HINTS) {
797 childResized();
798 } else if (ev.get_atom() == XEmbedInfo.getAtom()) {
799 processXEmbedInfo();
800 } else if (ev.get_atom() ==
801 XDnDConstants.XA_XdndAware.getAtom()) {
802 XDropTargetRegistry.getRegistry().unregisterXEmbedClient(getWindow(),
803 xembed.handle);
804 if (ev.get_state() == XConstants.PropertyNewValue) {
805 XDropTargetRegistry.getRegistry().registerXEmbedClient(getWindow(),
806 xembed.handle);
807 }
808 }
809 } else {
810 xembedLog.finer("XEmbed is not active");
811 }
812 }
813 void handleConfigureNotify(XEvent xev) {
814 if (isXEmbedActive()) {
815 XConfigureEvent ev = xev.get_xconfigure();
816 if (xembedLog.isLoggable(PlatformLogger.FINER)) {
817 xembedLog.finer("Bounds change on client: " + ev);
818 }
819 if (xev.get_xany().get_window() == handle) {
820 childResized();
821 }
822 }
823 }
824 public void dispatchEvent(XEvent xev) {
825 int type = xev.get_type();
826 switch (type) {
827 case XConstants.PropertyNotify:
828 handlePropertyNotify(xev);
829 break;
830 case XConstants.ConfigureNotify:
831 handleConfigureNotify(xev);
832 break;
833 case XConstants.ClientMessage:
834 handleClientMessage(xev);
835 break;
836 }
849 init(ev);
850 }
851
852 private void init(KeyEvent e) {
853 byte[] bdata = getBData(e);
854 long data = Native.toData(bdata);
855 if (data == 0) {
856 return;
857 }
858 try {
859 XToolkit.awtLock();
860 try {
861 keysym = XWindow.getKeySymForAWTKeyCode(e.getKeyCode());
862 } finally {
863 XToolkit.awtUnlock();
864 }
865 XKeyEvent ke = new XKeyEvent(data);
866
867 // We recognize only these masks
868 modifiers = ke.get_state() & (XConstants.ShiftMask | XConstants.ControlMask | XConstants.LockMask);
869 if (xembedLog.isLoggable(PlatformLogger.FINEST)) {
870 xembedLog.finest("Mapped " + e + " to " + this);
871 }
872 } finally {
873 XlibWrapper.unsafe.freeMemory(data);
874 }
875 }
876
877 public int hashCode() {
878 return (int)keysym & 0xFFFFFFFF;
879 }
880
881 public boolean equals(Object o) {
882 if (!(o instanceof GrabbedKey)) {
883 return false;
884 }
885 GrabbedKey key = (GrabbedKey)o;
886 return (keysym == key.keysym && modifiers == key.modifiers);
887 }
888
889 public String toString() {
|
106 applicationActive = toplevel.isFocused();
107 }
108 }
109
110 void deinstallActivateListener() {
111 Window toplevel = getTopLevel(target);
112 if (toplevel != null) {
113 toplevel.removeWindowFocusListener(this);
114 }
115 }
116
117 boolean isXEmbedActive() {
118 return xembed.handle != 0;
119 }
120
121 boolean isApplicationActive() {
122 return applicationActive;
123 }
124
125 void initDispatching() {
126 if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
127 xembedLog.fine("Init embedding for " + Long.toHexString(xembed.handle));
128 }
129 XToolkit.awtLock();
130 try {
131 XToolkit.addEventDispatcher(xembed.handle, xembed);
132 XlibWrapper.XSelectInput(XToolkit.getDisplay(), xembed.handle,
133 XConstants.StructureNotifyMask | XConstants.PropertyChangeMask);
134
135 XDropTargetRegistry.getRegistry().registerXEmbedClient(getWindow(), xembed.handle);
136 } finally {
137 XToolkit.awtUnlock();
138 }
139 xembed.processXEmbedInfo();
140
141 notifyChildEmbedded();
142 }
143
144 void endDispatching() {
145 if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
146 xembedLog.fine("End dispatching for " + Long.toHexString(xembed.handle));
147 }
148 XToolkit.awtLock();
149 try {
150 XDropTargetRegistry.getRegistry().unregisterXEmbedClient(getWindow(), xembed.handle);
151 // We can't deselect input since someone else might be interested in it
152 XToolkit.removeEventDispatcher(xembed.handle, xembed);
153 } finally {
154 XToolkit.awtUnlock();
155 }
156 }
157
158 void embedChild(long child) {
159 if (xembed.handle != 0) {
160 detachChild();
161 }
162 xembed.handle = child;
163 initDispatching();
164 }
165
166 void childDestroyed() {
167 if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
168 xembedLog.fine("Child " + Long.toHexString(xembed.handle) + " has self-destroyed.");
169 }
170 endDispatching();
171 xembed.handle = 0;
172 }
173
174 public void handleEvent(AWTEvent e) {
175 super.handleEvent(e);
176 if (isXEmbedActive()) {
177 switch (e.getID()) {
178 case FocusEvent.FOCUS_GAINED:
179 canvasFocusGained((FocusEvent)e);
180 break;
181 case FocusEvent.FOCUS_LOST:
182 canvasFocusLost((FocusEvent)e);
183 break;
184 case KeyEvent.KEY_PRESSED:
185 case KeyEvent.KEY_RELEASED:
186 if (!((InputEvent)e).isConsumed()) {
187 forwardKeyEvent((KeyEvent)e);
188 }
189 break;
190 }
191 }
192 }
193
194 public void dispatchEvent(XEvent ev) {
195 super.dispatchEvent(ev);
196 switch (ev.get_type()) {
197 case XConstants.CreateNotify:
198 XCreateWindowEvent cr = ev.get_xcreatewindow();
199 if (xembedLog.isLoggable(PlatformLogger.Level.FINEST)) {
200 xembedLog.finest("Message on embedder: " + cr);
201 }
202 if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
203 xembedLog.finer("Create notify for parent " + Long.toHexString(cr.get_parent()) +
204 ", window " + Long.toHexString(cr.get_window()));
205 }
206 embedChild(cr.get_window());
207 break;
208 case XConstants.DestroyNotify:
209 XDestroyWindowEvent dn = ev.get_xdestroywindow();
210 if (xembedLog.isLoggable(PlatformLogger.Level.FINEST)) {
211 xembedLog.finest("Message on embedder: " + dn);
212 }
213 if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
214 xembedLog.finer("Destroy notify for parent: " + dn);
215 }
216 childDestroyed();
217 break;
218 case XConstants.ReparentNotify:
219 XReparentEvent rep = ev.get_xreparent();
220 if (xembedLog.isLoggable(PlatformLogger.Level.FINEST)) {
221 xembedLog.finest("Message on embedder: " + rep);
222 }
223 if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
224 xembedLog.finer("Reparent notify for parent " + Long.toHexString(rep.get_parent()) +
225 ", window " + Long.toHexString(rep.get_window()) +
226 ", event " + Long.toHexString(rep.get_event()));
227 }
228 if (rep.get_parent() == getWindow()) {
229 // Reparented into us - embed it
230 embedChild(rep.get_window());
231 } else {
232 // Reparented out of us - detach it
233 childDestroyed();
234 }
235 break;
236 }
237 }
238
239 public Dimension getPreferredSize() {
240 if (isXEmbedActive()) {
241 XToolkit.awtLock();
242 try {
243 long p_hints = XlibWrapper.XAllocSizeHints();
306 xembed.handle, wattr.pData);
307
308 XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
309
310 if ((status == 0) ||
311 ((XErrorHandlerUtil.saved_error != null) &&
312 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success))) {
313 return null;
314 }
315
316 return new Rectangle(wattr.get_x(), wattr.get_y(), wattr.get_width(), wattr.get_height());
317 } finally {
318 wattr.dispose();
319 }
320 } finally {
321 XToolkit.awtUnlock();
322 }
323 }
324
325 void childResized() {
326 if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
327 Rectangle bounds = getClientBounds();
328 xembedLog.finer("Child resized: " + bounds);
329 // It is not required to update embedder's size when client size changes
330 // However, since there is no any means to get client size it seems to be the
331 // only way to provide it. However, it contradicts with Java layout concept -
332 // so it is disabled for now.
333 // Rectangle my_bounds = getBounds();
334 // setBounds(my_bounds.x, my_bounds.y, bounds.width, bounds.height, SET_BOUNDS);
335 }
336 XToolkit.postEvent(XToolkit.targetToAppContext(target), new ComponentEvent(target, ComponentEvent.COMPONENT_RESIZED));
337 }
338
339 void focusNext() {
340 if (isXEmbedActive()) {
341 xembedLog.fine("Requesting focus for the next component after embedder");
342 postEvent(new InvocationEvent(target, new Runnable() {
343 public void run() {
344 KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent(target);
345 }
346 }));
371 }
372 }));
373 } else {
374 xembedLog.fine("XEmbed is not active - denying request focus");
375 }
376 }
377
378 void notifyChildEmbedded() {
379 xembed.sendMessage(xembed.handle, XEMBED_EMBEDDED_NOTIFY, getWindow(), Math.min(xembed.version, XEMBED_VERSION), 0);
380 if (isApplicationActive()) {
381 xembedLog.fine("Sending WINDOW_ACTIVATE during initialization");
382 xembed.sendMessage(xembed.handle, XEMBED_WINDOW_ACTIVATE);
383 if (hasFocus()) {
384 xembedLog.fine("Sending FOCUS_GAINED during initialization");
385 xembed.sendMessage(xembed.handle, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0);
386 }
387 }
388 }
389
390 void detachChild() {
391 if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
392 xembedLog.fine("Detaching child " + Long.toHexString(xembed.handle));
393 }
394 /**
395 * XEmbed specification:
396 * "The embedder can unmap the client and reparent the client window to the root window. If the
397 * client receives an ReparentNotify event, it should check the parent field of the XReparentEvent
398 * structure. If this is the root window of the window's screen, then the protocol is finished and
399 * there is no further interaction. If it is a window other than the root window, then the protocol
400 * continues with the new parent acting as the embedder window."
401 */
402 XToolkit.awtLock();
403 try {
404 XlibWrapper.XUnmapWindow(XToolkit.getDisplay(), xembed.handle);
405 XlibWrapper.XReparentWindow(XToolkit.getDisplay(), xembed.handle, XToolkit.getDefaultRootWindow(), 0, 0);
406 } finally {
407 XToolkit.awtUnlock();
408 }
409 endDispatching();
410 xembed.handle = 0;
411 }
454 }
455 }
456 xembed.sendMessage(xembed.handle, XEMBED_FOCUS_OUT, num, 0, 0);
457 }
458 }
459
460 static byte[] getBData(KeyEvent e) {
461 return AWTAccessor.getAWTEventAccessor().getBData(e);
462 }
463
464 void forwardKeyEvent(KeyEvent e) {
465 xembedLog.fine("Try to forward key event");
466 byte[] bdata = getBData(e);
467 long data = Native.toData(bdata);
468 if (data == 0) {
469 return;
470 }
471 try {
472 XKeyEvent ke = new XKeyEvent(data);
473 ke.set_window(xembed.handle);
474 if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
475 xembedLog.fine("Forwarding native key event: " + ke);
476 }
477 XToolkit.awtLock();
478 try {
479 XlibWrapper.XSendEvent(XToolkit.getDisplay(), xembed.handle, false, XConstants.NoEventMask, data);
480 } finally {
481 XToolkit.awtUnlock();
482 }
483 } finally {
484 XlibWrapper.unsafe.freeMemory(data);
485 }
486 }
487
488
489 /**
490 * Grab/ungrab key functionality is an unofficial API supported by
491 * GTK. Unfortunately, it doesn't support accelerator API, so,
492 * since this is the ONLY shortcut-processing API available, we
493 * must support it. See XEmbed.NON_STANDARD_XEMBED_GTK_*
494 * messages. The format of these messages is as follows:
495 * - request from client:
496 * data[1] = NON_STANDARD_XEMBED_GTK_GRAB_KEY or NON_STANDARD_XEMBED_GTK_UNGRAB_KEY
497 * data[3] = X keysym
498 * data[4] = X modifiers
499 *
500 * - response from server (in case the grabbed key has been pressed):
501 * forwarded XKeyEvent that matches keysym/modifiers pair
502 */
503 void grabKey(final long keysym, final long modifiers) {
504 postEvent(new InvocationEvent(target, new Runnable() {
505 public void run() {
506 GrabbedKey grab = new GrabbedKey(keysym, modifiers);
507 if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
508 xembedLog.fine("Grabbing key: " + grab);
509 }
510 synchronized(GRAB_LOCK) {
511 grabbed_keys.add(grab);
512 }
513 }
514 }));
515 }
516
517 void ungrabKey(final long keysym, final long modifiers) {
518 postEvent(new InvocationEvent(target, new Runnable() {
519 public void run() {
520 GrabbedKey grab = new GrabbedKey(keysym, modifiers);
521 if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
522 xembedLog.fine("UnGrabbing key: " + grab);
523 }
524 synchronized(GRAB_LOCK) {
525 grabbed_keys.remove(grab);
526 }
527 }
528 }));
529 }
530
531 void registerAccelerator(final long accel_id, final long keysym, final long modifiers) {
532 postEvent(new InvocationEvent(target, new Runnable() {
533 public void run() {
534 AWTKeyStroke stroke = xembed.getKeyStrokeForKeySym(keysym, modifiers);
535 if (stroke != null) {
536 if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
537 xembedLog.fine("Registering accelerator " + accel_id + " for " + stroke);
538 }
539 synchronized(ACCEL_LOCK) {
540 accelerators.put(accel_id, stroke);
541 accel_lookup.put(stroke, accel_id);
542 }
543 }
544 propogateRegisterAccelerator(stroke);
545 }
546 }));
547 }
548
549 void unregisterAccelerator(final long accel_id) {
550 postEvent(new InvocationEvent(target, new Runnable() {
551 public void run() {
552 AWTKeyStroke stroke = null;
553 synchronized(ACCEL_LOCK) {
554 stroke = accelerators.get(accel_id);
555 if (stroke != null) {
556 if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
557 xembedLog.fine("Unregistering accelerator: " + accel_id);
558 }
559 accelerators.remove(accel_id);
560 accel_lookup.remove(stroke); // FIXME: How about several accelerators with the same stroke?
561 }
562 }
563 propogateUnRegisterAccelerator(stroke);
564 }
565 }));
566 }
567
568 void propogateRegisterAccelerator(AWTKeyStroke stroke) {
569 // Find the top-level and see if it is XEmbed client. If so, ask him to
570 // register the accelerator
571 XWindowPeer parent = getToplevelXWindow();
572 if (parent != null && parent instanceof XEmbeddedFramePeer) {
573 XEmbeddedFramePeer embedded = (XEmbeddedFramePeer)parent;
574 embedded.registerAccelerator(stroke);
575 }
576 }
584 embedded.unregisterAccelerator(stroke);
585 }
586 }
587
588 public boolean postProcessKeyEvent(KeyEvent e) {
589 // Processing events only if we are in the focused window but
590 // we are not focus owner since otherwise we will get
591 // duplicate shortcut events in the client - one is from
592 // activate_accelerator, another from forwarded event
593 // FIXME: This is probably an incompatibility, protocol
594 // doesn't say anything about disable accelerators when client
595 // is focused.
596
597 XWindowPeer parent = getToplevelXWindow();
598 if (parent == null || !((Window)parent.getTarget()).isFocused() || target.isFocusOwner()) {
599 return false;
600 }
601
602 boolean result = false;
603
604 if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
605 xembedLog.finer("Post-processing event " + e);
606 }
607
608 // Process ACCELERATORS
609 AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
610 long accel_id = 0;
611 boolean exists = false;
612 synchronized(ACCEL_LOCK) {
613 exists = accel_lookup.containsKey(stroke);
614 if (exists) {
615 accel_id = accel_lookup.get(stroke).longValue();
616 }
617 }
618 if (exists) {
619 if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
620 xembedLog.fine("Activating accelerator " + accel_id);
621 }
622 xembed.sendMessage(xembed.handle, XEMBED_ACTIVATE_ACCELERATOR, accel_id, 0, 0); // FIXME: How about overloaded?
623 result = true;
624 }
625
626 // Process Grabs, unofficial GTK feature
627 exists = false;
628 GrabbedKey key = new GrabbedKey(e);
629 synchronized(GRAB_LOCK) {
630 exists = grabbed_keys.contains(key);
631 }
632 if (exists) {
633 if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
634 xembedLog.fine("Forwarding grabbed key " + e);
635 }
636 forwardKeyEvent(e);
637 result = true;
638 }
639
640 return result;
641 }
642
643 public void modalityPushed(ModalityEvent ev) {
644 xembed.sendMessage(xembed.handle, XEMBED_MODALITY_ON);
645 }
646
647 public void modalityPopped(ModalityEvent ev) {
648 xembed.sendMessage(xembed.handle, XEMBED_MODALITY_OFF);
649 }
650
651 public void handleClientMessage(XEvent xev) {
652 super.handleClientMessage(xev);
653 XClientMessageEvent msg = xev.get_xclient();
654 if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
655 xembedLog.finer("Client message to embedder: " + msg);
656 }
657 if (msg.get_message_type() == xembed.XEmbed.getAtom()) {
658 if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) {
659 xembedLog.fine(xembed.XEmbedMessageToString(msg));
660 }
661 }
662 if (isXEmbedActive()) {
663 switch ((int)msg.get_data(1)) {
664 case XEMBED_REQUEST_FOCUS:
665 requestXEmbedFocus();
666 break;
667 case XEMBED_FOCUS_NEXT:
668 focusNext();
669 break;
670 case XEMBED_FOCUS_PREV:
671 focusPrev();
672 break;
673 case XEMBED_REGISTER_ACCELERATOR:
674 registerAccelerator(msg.get_data(2), msg.get_data(3), msg.get_data(4));
675 break;
676 case XEMBED_UNREGISTER_ACCELERATOR:
677 unregisterAccelerator(msg.get_data(2));
678 break;
706 public void run() {
707 target.setDropTarget(new XEmbedDropTarget());
708 }
709 };
710 SunToolkit.executeOnEventHandlerThread(target, r);
711 }
712
713 public void removeXEmbedDropTarget() {
714 // Unregister a drop site on the top level.
715 Runnable r = new Runnable() {
716 public void run() {
717 if (target.getDropTarget() instanceof XEmbedDropTarget) {
718 target.setDropTarget(null);
719 }
720 }
721 };
722 SunToolkit.executeOnEventHandlerThread(target, r);
723 }
724
725 public boolean processXEmbedDnDEvent(long ctxt, int eventID) {
726 if (xembedLog.isLoggable(PlatformLogger.Level.FINEST)) {
727 xembedLog.finest(" Drop target=" + target.getDropTarget());
728 }
729 if (target.getDropTarget() instanceof XEmbedDropTarget) {
730 AppContext appContext = XToolkit.targetToAppContext(getTarget());
731 XDropTargetContextPeer peer =
732 XDropTargetContextPeer.getPeer(appContext);
733 peer.forwardEventToEmbedded(xembed.handle, ctxt, eventID);
734 return true;
735 } else {
736 return false;
737 }
738 }
739
740 class XEmbedServer extends XEmbedHelper implements XEventDispatcher {
741 long handle; // Handle to XEmbed client
742 long version;
743 long flags;
744
745 boolean processXEmbedInfo() {
746 long xembed_info_data = Native.allocateLongArray(2);
747 try {
748 if (!XEmbedInfo.getAtomData(handle, xembed_info_data, 2)) {
749 // No more XEMBED_INFO? This is not XEmbed client!
750 // Unfortunately this is the initial state of the most clients
751 // FIXME: add 5-state processing
752 //childDestroyed();
753 xembedLog.finer("Unable to get XEMBED_INFO atom data");
754 return false;
755 }
756 version = Native.getCard32(xembed_info_data, 0);
757 flags = Native.getCard32(xembed_info_data, 1);
758 boolean new_mapped = (flags & XEMBED_MAPPED) != 0;
759 boolean currently_mapped = XlibUtil.getWindowMapState(handle) != XConstants.IsUnmapped;
760 if (new_mapped != currently_mapped) {
761 if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
762 xembedLog.finer("Mapping state of the client has changed, old state: " + currently_mapped + ", new state: " + new_mapped);
763 }
764 if (new_mapped) {
765 XToolkit.awtLock();
766 try {
767 XlibWrapper.XMapWindow(XToolkit.getDisplay(), handle);
768 } finally {
769 XToolkit.awtUnlock();
770 }
771 } else {
772 XToolkit.awtLock();
773 try {
774 XlibWrapper.XUnmapWindow(XToolkit.getDisplay(), handle);
775 } finally {
776 XToolkit.awtUnlock();
777 }
778 }
779 } else {
780 if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
781 xembedLog.finer("Mapping state didn't change, mapped: " + currently_mapped);
782 }
783 }
784 return true;
785 } finally {
786 XlibWrapper.unsafe.freeMemory(xembed_info_data);
787 }
788 }
789
790 public void handlePropertyNotify(XEvent xev) {
791 if (isXEmbedActive()) {
792 XPropertyEvent ev = xev.get_xproperty();
793 if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
794 xembedLog.finer("Property change on client: " + ev);
795 }
796 if (ev.get_atom() == XAtom.XA_WM_NORMAL_HINTS) {
797 childResized();
798 } else if (ev.get_atom() == XEmbedInfo.getAtom()) {
799 processXEmbedInfo();
800 } else if (ev.get_atom() ==
801 XDnDConstants.XA_XdndAware.getAtom()) {
802 XDropTargetRegistry.getRegistry().unregisterXEmbedClient(getWindow(),
803 xembed.handle);
804 if (ev.get_state() == XConstants.PropertyNewValue) {
805 XDropTargetRegistry.getRegistry().registerXEmbedClient(getWindow(),
806 xembed.handle);
807 }
808 }
809 } else {
810 xembedLog.finer("XEmbed is not active");
811 }
812 }
813 void handleConfigureNotify(XEvent xev) {
814 if (isXEmbedActive()) {
815 XConfigureEvent ev = xev.get_xconfigure();
816 if (xembedLog.isLoggable(PlatformLogger.Level.FINER)) {
817 xembedLog.finer("Bounds change on client: " + ev);
818 }
819 if (xev.get_xany().get_window() == handle) {
820 childResized();
821 }
822 }
823 }
824 public void dispatchEvent(XEvent xev) {
825 int type = xev.get_type();
826 switch (type) {
827 case XConstants.PropertyNotify:
828 handlePropertyNotify(xev);
829 break;
830 case XConstants.ConfigureNotify:
831 handleConfigureNotify(xev);
832 break;
833 case XConstants.ClientMessage:
834 handleClientMessage(xev);
835 break;
836 }
849 init(ev);
850 }
851
852 private void init(KeyEvent e) {
853 byte[] bdata = getBData(e);
854 long data = Native.toData(bdata);
855 if (data == 0) {
856 return;
857 }
858 try {
859 XToolkit.awtLock();
860 try {
861 keysym = XWindow.getKeySymForAWTKeyCode(e.getKeyCode());
862 } finally {
863 XToolkit.awtUnlock();
864 }
865 XKeyEvent ke = new XKeyEvent(data);
866
867 // We recognize only these masks
868 modifiers = ke.get_state() & (XConstants.ShiftMask | XConstants.ControlMask | XConstants.LockMask);
869 if (xembedLog.isLoggable(PlatformLogger.Level.FINEST)) {
870 xembedLog.finest("Mapped " + e + " to " + this);
871 }
872 } finally {
873 XlibWrapper.unsafe.freeMemory(data);
874 }
875 }
876
877 public int hashCode() {
878 return (int)keysym & 0xFFFFFFFF;
879 }
880
881 public boolean equals(Object o) {
882 if (!(o instanceof GrabbedKey)) {
883 return false;
884 }
885 GrabbedKey key = (GrabbedKey)o;
886 return (keysym == key.keysym && modifiers == key.modifiers);
887 }
888
889 public String toString() {
|