1 /* XEventPump.java -- Pumps events from X to AWT
2 Copyright (C) 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package gnu.java.awt.peer.x;
41 import java.awt.AWTEvent;
42 import java.awt.Component;
43 import java.awt.Container;
44 import java.awt.Graphics;
45 import java.awt.Insets;
46 import java.awt.Rectangle;
47 import java.awt.Toolkit;
48 import java.awt.Window;
49 import java.awt.event.ComponentEvent;
50 import java.awt.event.KeyEvent;
51 import java.awt.event.MouseEvent;
52 import java.awt.event.PaintEvent;
53 import java.awt.event.WindowEvent;
54 import java.util.HashMap;
56 import gnu.java.awt.ComponentReshapeEvent;
58 import gnu.x11.Display;
59 import gnu.x11.event.ButtonPress;
60 import gnu.x11.event.ButtonRelease;
61 import gnu.x11.event.ClientMessage;
62 import gnu.x11.event.ConfigureNotify;
63 import gnu.x11.event.DestroyNotify;
64 import gnu.x11.event.Event;
65 import gnu.x11.event.Expose;
66 import gnu.x11.event.Input;
67 import gnu.x11.event.KeyPress;
68 import gnu.x11.event.KeyRelease;
69 import gnu.x11.event.MotionNotify;
70 import gnu.x11.event.PropertyNotify;
71 import gnu.x11.event.ResizeRequest;
72 import gnu.x11.event.UnmapNotify;
75 * Fetches events from X, translates them to AWT events and pumps them up
76 * into the AWT event queue.
78 * @author Roman Kennke (kennke@aicas.com)
80 public class XEventPump
85 * The X Display from which we fetch and pump up events.
87 private Display display;
90 * Maps X Windows to AWT Windows to be able to correctly determine the
93 private HashMap windows;
96 * Indicates if we are currently inside a drag operation. This is
97 * set to the button ID when a button is pressed and to -1 (indicating
98 * that no drag is active) when the mouse is released.
103 * Creates a new XEventPump for the specified X Display.
105 * @param d the X Display
107 XEventPump(Display d)
110 windows = new HashMap();
112 Thread thread = new Thread(this, "X Event Pump");
113 thread.setDaemon(true);
118 * The main event pump loop. This basically fetches events from the
119 * X Display and pumps them into the system event queue.
123 while (display.connected)
127 Event xEvent = display.next_event();
130 catch (ThreadDeath death)
132 // If someone wants to kill us, let them.
137 System.err.println("Exception during event dispatch:");
138 x.printStackTrace(System.err);
144 * Adds an X Window to AWT Window mapping. This is required so that the
145 * event pump can correctly determine the event targets.
147 * @param xWindow the X Window
148 * @param awtWindow the AWT Window
150 void registerWindow(gnu.x11.Window xWindow, Window awtWindow)
153 System.err.println("registering window id: " + xWindow.id);
154 windows.put(new Integer(xWindow.id), awtWindow);
157 void unregisterWindow(gnu.x11.Window xWindow)
159 windows.remove(new Integer(xWindow.id));
162 private void handleButtonPress(ButtonPress event)
164 Integer key = new Integer(event.getEventWindowID());
165 Window awtWindow = (Window) windows.get(key);
167 // Create and post the mouse event.
168 int button = event.detail();
170 // AWT cannot handle more than 3 buttons and expects 0 instead.
171 if (button >= gnu.x11.Input.BUTTON3)
176 findMouseEventTarget(awtWindow, event.getEventX(), event.getEventY());
182 MouseEvent mp = new MouseEvent(target, MouseEvent.MOUSE_PRESSED,
183 System.currentTimeMillis(),
184 KeyboardMapping.mapModifiers(event.getState())
185 | buttonToModifier(button),
186 event.getEventX(), event.getEventY(),
188 Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(mp);
191 private void handleButtonRelease(ButtonRelease event)
193 Integer key = new Integer(event.getEventWindowID());
194 Window awtWindow = (Window) windows.get(key);
196 int button = event.detail();
198 // AWT cannot handle more than 3 buttons and expects 0 instead.
199 if (button >= gnu.x11.Input.BUTTON3)
204 findMouseEventTarget(awtWindow, event.getEventX(), event.getEventY());
210 MouseEvent mr = new MouseEvent(target, MouseEvent.MOUSE_RELEASED,
211 System.currentTimeMillis(),
212 KeyboardMapping.mapModifiers(event.getState())
213 | buttonToModifier(button),
214 event.getEventX(), event.getEventY(),
216 Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(mr);
220 private void handleMotionNotify(MotionNotify event)
222 Integer key = new Integer(event.getEventWindowID());
223 Window awtWindow = (Window) windows.get(key);
225 int button = event.detail();
227 // AWT cannot handle more than 3 buttons and expects 0 instead.
228 if (button >= gnu.x11.Input.BUTTON3)
231 MouseEvent mm = null;
234 mm = new MouseEvent(awtWindow, MouseEvent.MOUSE_MOVED,
235 System.currentTimeMillis(),
236 KeyboardMapping.mapModifiers(event.getState())
237 | buttonToModifier(button),
238 event.getEventX(), event.getEventY(),
244 mm = new MouseEvent(awtWindow, MouseEvent.MOUSE_DRAGGED,
245 System.currentTimeMillis(),
246 KeyboardMapping.mapModifiers(event.getState())
247 | buttonToModifier(drag),
248 event.getEventX(), event.getEventY(),
251 Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(mm);
254 // FIME: refactor and make faster, maybe caching the event and handle
255 // and/or check timing (timing is generated for PropertyChange)?
256 private void handleExpose(Expose event)
258 Integer key = new Integer(event.window_id);
259 Window awtWindow = (Window) windows.get(key);
262 System.err.println("expose request for window id: " + key);
264 Rectangle r = new Rectangle(event.x(), event.y(), event.width(),
266 // We need to clear the background of the exposed rectangle.
267 assert awtWindow != null : "awtWindow == null for window ID: " + key;
269 Graphics g = awtWindow.getGraphics();
270 g.clearRect(r.x, r.y, r.width, r.height);
273 XWindowPeer xwindow = (XWindowPeer) awtWindow.getPeer();
274 Insets i = xwindow.insets();
275 if (event.width() != awtWindow.getWidth() - i.left - i.right
276 || event.height() != awtWindow.getHeight() - i.top - i.bottom)
278 int w = event.width();
279 int h = event.height();
280 int x = xwindow.xwindow.x;
281 int y = xwindow.xwindow.y;
284 System.err.println("Setting size on AWT window: " + w
285 + ", " + h + ", " + awtWindow.getWidth()
286 + ", " + awtWindow.getHeight());
288 // new width and height
289 xwindow.xwindow.width = w;
290 xwindow.xwindow.height = h;
292 // reshape the window
293 ComponentReshapeEvent cre =
294 new ComponentReshapeEvent(awtWindow, x, y, w, h);
295 awtWindow.dispatchEvent(cre);
299 new ComponentEvent(awtWindow, ComponentEvent.COMPONENT_RESIZED);
300 awtWindow.dispatchEvent(ce);
302 PaintEvent pev = new PaintEvent(awtWindow, PaintEvent.UPDATE, r);
303 Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(pev);
306 private void handleDestroyNotify(DestroyNotify destroyNotify)
309 System.err.println("DestroyNotify event: " + destroyNotify);
311 Integer key = new Integer(destroyNotify.event_window_id);
312 Window awtWindow = (Window) windows.get(key);
314 AWTEvent event = new WindowEvent(awtWindow, WindowEvent.WINDOW_CLOSED);
315 Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(event);
318 private void handleClientMessage(ClientMessage clientMessage)
321 System.err.println("ClientMessage event: " + clientMessage);
323 if (clientMessage.delete_window())
326 System.err.println("ClientMessage is a delete_window event");
328 Integer key = new Integer(clientMessage.window_id);
329 Window awtWindow = (Window) windows.get(key);
331 AWTEvent event = new WindowEvent(awtWindow, WindowEvent.WINDOW_CLOSING);
332 Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(event);
336 private void handleEvent(Event xEvent)
339 System.err.println("fetched event: " + xEvent);
341 switch (xEvent.code() & 0x7f)
343 case ButtonPress.CODE:
344 this.handleButtonPress((ButtonPress) xEvent);
346 case ButtonRelease.CODE:
347 this.handleButtonRelease((ButtonRelease) xEvent);
349 case MotionNotify.CODE:
350 this.handleMotionNotify((MotionNotify) xEvent);
353 this.handleExpose((Expose) xEvent);
356 case KeyRelease.CODE:
357 Integer key = new Integer(((Input) xEvent).getEventWindowID());
358 Window awtWindow = (Window) windows.get(key);
359 handleKeyEvent(xEvent, awtWindow);
361 case DestroyNotify.CODE:
362 this.handleDestroyNotify((DestroyNotify) xEvent);
364 case ClientMessage.CODE:
365 this.handleClientMessage((ClientMessage) xEvent);
367 case PropertyNotify.CODE:
368 key = new Integer (((PropertyNotify) xEvent).getWindowID());
369 awtWindow = (Window) windows.get(key);
370 AWTEvent event = new WindowEvent(awtWindow, WindowEvent.WINDOW_STATE_CHANGED);
371 Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(event);
375 System.err.println("Unhandled X event: " + xEvent);
380 * Handles key events from X.
382 * @param xEvent the X event
383 * @param awtWindow the AWT window to which the event gets posted
385 private void handleKeyEvent(Event xEvent, Window awtWindow)
387 Input keyEvent = (Input) xEvent;
388 int xKeyCode = keyEvent.detail();
389 int xMods = keyEvent.getState();
390 int keyCode = KeyboardMapping.mapToKeyCode(xEvent.display.input, xKeyCode,
392 char keyChar = KeyboardMapping.mapToKeyChar(xEvent.display.input, xKeyCode,
395 System.err.println("XEventPump.handleKeyEvent: " + xKeyCode + ", "
396 + xMods + ": " + ((int) keyChar) + ", " + keyCode);
397 int awtMods = KeyboardMapping.mapModifiers(xMods);
398 long when = System.currentTimeMillis();
400 if (keyEvent.code() == KeyPress.CODE)
402 ke = new KeyEvent(awtWindow, KeyEvent.KEY_PRESSED, when,
404 KeyEvent.CHAR_UNDEFINED);
405 Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ke);
406 if (keyChar != KeyEvent.CHAR_UNDEFINED)
408 ke = new KeyEvent(awtWindow, KeyEvent.KEY_TYPED, when,
409 awtMods, KeyEvent.VK_UNDEFINED,
411 Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ke);
417 ke = new KeyEvent(awtWindow, KeyEvent.KEY_RELEASED, when,
419 KeyEvent.CHAR_UNDEFINED);
420 Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ke);
425 /** Translates an X button identifier to the AWT's MouseEvent modifier
426 * mask. As the AWT cannot handle more than 3 buttons those return
429 static int buttonToModifier(int button)
433 case gnu.x11.Input.BUTTON1:
434 return MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON1_MASK;
435 case gnu.x11.Input.BUTTON2:
436 return MouseEvent.BUTTON2_DOWN_MASK | MouseEvent.BUTTON2_MASK;
437 case gnu.x11.Input.BUTTON3:
438 return MouseEvent.BUTTON3_DOWN_MASK | MouseEvent.BUTTON3_MASK;
445 * Finds the heavyweight mouse event target.
447 * @param src the original source of the event
449 * @param pt the event coordinates
451 * @return the real mouse event target
453 private Component findMouseEventTarget(Component src, int x, int y)
455 Component found = null;
456 if (src instanceof Container)
458 Container cont = (Container) src;
459 int numChildren = cont.getComponentCount();
460 for (int i = 0; i < numChildren && found == null; i++)
462 Component child = cont.getComponent(i);
463 if (child != null && child.isVisible()
464 && child.contains(x - child.getX(), y - child.getY()))
466 if (child instanceof Container)
468 Component deeper = findMouseEventTarget(child,
474 else if (! child.isLightweight())
480 // Consider the source itself.
481 if (found == null && src.contains(x, y) && ! src.isLightweight())