269 lines
6.3 KiB
Java
269 lines
6.3 KiB
Java
|
package gnu.awt.xlib;
|
||
|
|
||
|
/* Copyright (C) 2000 Free Software Foundation
|
||
|
|
||
|
This file is part of libgcj.
|
||
|
|
||
|
This software is copyrighted work licensed under the terms of the
|
||
|
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
|
||
|
details. */
|
||
|
|
||
|
import java.awt.*;
|
||
|
|
||
|
import gnu.awt.LightweightRedirector;
|
||
|
import gnu.gcj.xlib.Display;
|
||
|
import gnu.gcj.xlib.XAnyEvent;
|
||
|
import gnu.gcj.xlib.XExposeEvent;
|
||
|
import gnu.gcj.xlib.XButtonEvent;
|
||
|
import gnu.gcj.xlib.XConfigureEvent;
|
||
|
import java.awt.event.PaintEvent;
|
||
|
import java.awt.event.InputEvent;
|
||
|
import java.awt.event.MouseEvent;
|
||
|
import java.util.Vector;
|
||
|
|
||
|
public class XEventLoop implements Runnable
|
||
|
{
|
||
|
Display display;
|
||
|
EventQueue queue;
|
||
|
XAnyEvent anyEvent;
|
||
|
Thread eventLoopThread;
|
||
|
|
||
|
LightweightRedirector lightweightRedirector = new LightweightRedirector();
|
||
|
|
||
|
public XEventLoop(Display display, EventQueue queue)
|
||
|
{
|
||
|
this.display = display;
|
||
|
this.queue = queue;
|
||
|
|
||
|
anyEvent = new XAnyEvent(display);
|
||
|
eventLoopThread = new Thread(this, "AWT thread for XEventLoop");
|
||
|
eventLoopThread.start();
|
||
|
}
|
||
|
|
||
|
public void run()
|
||
|
{
|
||
|
while (true)
|
||
|
postNextEvent();
|
||
|
}
|
||
|
|
||
|
void postNextEvent()
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
AWTEvent evt = getNextEvent();
|
||
|
queue.postEvent(evt);
|
||
|
|
||
|
}
|
||
|
catch (InterruptedException ie)
|
||
|
{
|
||
|
// FIXME: what now?
|
||
|
System.err.println(ie);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** get next event. Will block until events become available. */
|
||
|
|
||
|
public AWTEvent getNextEvent()
|
||
|
{
|
||
|
// ASSERT:
|
||
|
if (isIdle())
|
||
|
throw new Error("should not be idle");
|
||
|
|
||
|
AWTEvent event = null;
|
||
|
while (event == null)
|
||
|
{
|
||
|
loadNextEvent();
|
||
|
event = createEvent();
|
||
|
}
|
||
|
|
||
|
event = lightweightRedirector.redirect(event);
|
||
|
|
||
|
return event;
|
||
|
}
|
||
|
|
||
|
void loadNextEvent()
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
setIdle(true);
|
||
|
|
||
|
/* The code below will result in an XFlush(). However,
|
||
|
while we are waiting for events after calling XFlush(),
|
||
|
new X requests issued on other threads will not
|
||
|
automatically be flushed. This can lead to a deadlock
|
||
|
since XFlush() will not be called before after the
|
||
|
processing of the next event, and new events arriving
|
||
|
might be dependent on the delivery of the X
|
||
|
requests.
|
||
|
|
||
|
Code that issues X requests should therefore call
|
||
|
flushIfIdle() after they're done, to ensure that the
|
||
|
requests are delivered in a timely manner. XFlush is not
|
||
|
run if event processing is underway, since we are assured
|
||
|
that the event loop execution will return to this point,
|
||
|
where requests are flushed again, before waiting for new
|
||
|
events.
|
||
|
|
||
|
Alternatively, do the work on the AWT thread, since the
|
||
|
XEventQueue knows how to flush the display when it runs out
|
||
|
of events. */
|
||
|
|
||
|
//display.flush(); // implicit?
|
||
|
anyEvent.loadNext();
|
||
|
}
|
||
|
catch (RuntimeException re)
|
||
|
{
|
||
|
System.err.println("Exception thrown on event thread:" + re);
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
setIdle(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @returns an AWT event created based on the current XEvent.
|
||
|
* Returns null if the current XEvent does not map to any perticular
|
||
|
* AWT event.
|
||
|
*/
|
||
|
|
||
|
AWTEvent createEvent()
|
||
|
{
|
||
|
/* avoid attempting to get client data before client data has
|
||
|
been set. */
|
||
|
Object peer;
|
||
|
synchronized (this)
|
||
|
{
|
||
|
peer = anyEvent.getWindow().getClientData();
|
||
|
}
|
||
|
|
||
|
Component source = null;
|
||
|
|
||
|
// Try to identify source component
|
||
|
|
||
|
if (peer instanceof XCanvasPeer)
|
||
|
{
|
||
|
source = ((XCanvasPeer) peer).getComponent();
|
||
|
}
|
||
|
|
||
|
if (source == null)
|
||
|
{
|
||
|
String msg = "unable to locate source for event (" +
|
||
|
anyEvent + ")";
|
||
|
throw new RuntimeException(msg);
|
||
|
}
|
||
|
|
||
|
/* if a mapping from anyEvent to AWTEvent is possible, construct a
|
||
|
new AWTEvent and return it. */
|
||
|
|
||
|
int type = anyEvent.getType();
|
||
|
switch (type)
|
||
|
{
|
||
|
case XAnyEvent.TYPE_EXPOSE:
|
||
|
return createPaintEvent(source);
|
||
|
case XAnyEvent.TYPE_BUTTON_PRESS:
|
||
|
case XAnyEvent.TYPE_BUTTON_RELEASE:
|
||
|
return createMouseEvent(type, source);
|
||
|
case XAnyEvent.TYPE_UNMAP_NOTIFY:
|
||
|
case XAnyEvent.TYPE_MAP_NOTIFY:
|
||
|
case XAnyEvent.TYPE_REPARENT_NOTIFY:
|
||
|
return null; // ignore for now
|
||
|
case XAnyEvent.TYPE_CONFIGURE_NOTIFY:
|
||
|
configureNotify(peer);
|
||
|
return null;
|
||
|
|
||
|
default:
|
||
|
String msg = "Do no know how to handle event (" + anyEvent + ")";
|
||
|
throw new RuntimeException(msg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
AWTEvent createPaintEvent(Component src)
|
||
|
{
|
||
|
XExposeEvent expose = new XExposeEvent(anyEvent);
|
||
|
PaintEvent pe = new PaintEvent(src, PaintEvent.PAINT,
|
||
|
expose.getBounds());
|
||
|
return pe;
|
||
|
}
|
||
|
|
||
|
AWTEvent createMouseEvent(int type, Component src)
|
||
|
{
|
||
|
XButtonEvent buttonEvt = new XButtonEvent(anyEvent);
|
||
|
|
||
|
int modifiers = 0; //buttonToModifierMap[buttonEvt.button];
|
||
|
|
||
|
/* Warning: this makes assumptions on the contents of
|
||
|
X.h... Button1 = 1, Button2 = 2, etc... */
|
||
|
switch (buttonEvt.button)
|
||
|
{
|
||
|
case 1:
|
||
|
modifiers = InputEvent.BUTTON1_MASK;
|
||
|
break;
|
||
|
case 2:
|
||
|
modifiers = InputEvent.BUTTON2_MASK;
|
||
|
break;
|
||
|
case 3:
|
||
|
modifiers = InputEvent.BUTTON2_MASK;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
int state = buttonEvt.state;
|
||
|
|
||
|
// remap bits from state to modifiers:
|
||
|
|
||
|
if ((state & XButtonEvent.MASK_SHIFT) != 0)
|
||
|
modifiers |= InputEvent.SHIFT_MASK;
|
||
|
|
||
|
|
||
|
if ((state & XButtonEvent.MASK_CONTROL) != 0)
|
||
|
modifiers |= InputEvent.CTRL_MASK;
|
||
|
|
||
|
|
||
|
/* FIXME: we need additional X code to properly map MODn states to
|
||
|
input modifiers */
|
||
|
|
||
|
int clickCount = 1; // FIXME... Can't get this from X.
|
||
|
boolean popupTrigger = false; // FIXME: look up policy somewhere
|
||
|
|
||
|
int x = buttonEvt.x;
|
||
|
int y = buttonEvt.y;
|
||
|
|
||
|
int id = (type == XAnyEvent.TYPE_BUTTON_PRESS) ?
|
||
|
MouseEvent.MOUSE_PRESSED :
|
||
|
MouseEvent.MOUSE_RELEASED;
|
||
|
|
||
|
MouseEvent me = new MouseEvent(src,
|
||
|
id,
|
||
|
buttonEvt.time, modifiers,
|
||
|
buttonEvt.x, buttonEvt.y,
|
||
|
clickCount, popupTrigger);
|
||
|
return me;
|
||
|
}
|
||
|
|
||
|
void configureNotify(Object peerObj)
|
||
|
{
|
||
|
XConfigureEvent configEvent = new XConfigureEvent(anyEvent);
|
||
|
XFramePeer peer = (XFramePeer) peerObj;
|
||
|
|
||
|
peer.configureNotify(configEvent);
|
||
|
}
|
||
|
|
||
|
public void flushIfIdle()
|
||
|
{
|
||
|
if (isIdle())
|
||
|
display.flush();
|
||
|
}
|
||
|
|
||
|
volatile boolean idle = false;
|
||
|
|
||
|
final synchronized void setIdle(boolean idle)
|
||
|
{
|
||
|
this.idle = idle;
|
||
|
}
|
||
|
|
||
|
final synchronized boolean isIdle()
|
||
|
{
|
||
|
return idle;
|
||
|
}
|
||
|
}
|