/*
* This file is part of the Haven & Hearth game client.
* Copyright (C) 2009 Fredrik Tolf <fredrik@dolda2000.com>, and
* Björn Johannessen <johannessen.bjorn@gmail.com>
*
* Redistribution and/or modification of this file is subject to the
* terms of the GNU Lesser General Public License, version 3, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Other parts of this source tree adhere to other copying
* rights. Please see the file `COPYING' in the root directory of the
* source tree for details.
*
* A copy the GNU Lesser General Public License is distributed along
* with the source tree of which this file is a part in the file
* `doc/LPGL-3'. If it is missing for any reason, please see the Free
* Software Foundation's website at <http://www.fsf.org/>, or write
* to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*/
package haven;
import java.awt.Cursor;
import java.awt.GraphicsConfiguration;
import java.awt.Toolkit;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLException;
import javax.media.opengl.awt.GLCanvas;
public class HavenPanel extends GLCanvas implements Runnable {
public UI ui;
boolean inited = false, rdr = false;
int w, h;
long fd = 20, fps = 0;
int dth = 0, dtm = 0;
public static int texhit = 0, texmiss = 0;
Queue<InputEvent> events = new LinkedList<InputEvent>();
private String cursmode = "tex";
private Resource lastcursor = null;
public Coord mousepos = new Coord(0, 0);
public Profile prof = new Profile(300);
private Profile.Frame curf = null;
private SyncFSM fsm = null;
private static final GLCapabilities caps;
static {
caps = new GLCapabilities(null);
caps.setDoubleBuffered(true);
caps.setAlphaBits(8);
caps.setRedBits(8);
caps.setGreenBits(8);
caps.setBlueBits(8);
}
public HavenPanel(int w, int h) {
super(caps);
setSize(this.w = w, this.h = h);
initgl();
if (Toolkit.getDefaultToolkit().getMaximumCursorColors() >= 256)
cursmode = "awt";
setCursor(Toolkit.getDefaultToolkit().createCustomCursor(TexI.mkbuf(new Coord(1, 1)), new java.awt.Point(), ""));
}
private void initgl() {
final Thread caller = Thread.currentThread();
addGLEventListener(new GLEventListener() {
public void display(GLAutoDrawable d) {
GL2 gl = (GL2) d.getGL();
if (inited && rdr)
redraw(gl);
TexGL.disposeall(gl);
}
public void init(GLAutoDrawable d) {
GL2 gl = (GL2) d.getGL();
if (caller.getThreadGroup() instanceof haven.error.ErrorHandler) {
haven.error.ErrorHandler h = (haven.error.ErrorHandler) caller.getThreadGroup();
h.lsetprop("gl.vendor", gl.glGetString(GL.GL_VENDOR));
h.lsetprop("gl.version", gl.glGetString(GL.GL_VERSION));
h.lsetprop("gl.renderer", gl.glGetString(GL.GL_RENDERER));
h.lsetprop("gl.exts", Arrays.asList(gl.glGetString(GL.GL_EXTENSIONS).split(" ")));
h.lsetprop("gl.caps", d.getChosenGLCapabilities().toString());
}
gl.glColor3f(1, 1, 1);
gl.glPointSize(4);
gl.setSwapInterval(1);
gl.glEnable(GL.GL_BLEND);
// gl.glEnable(GL.GL_LINE_SMOOTH);
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
int[] maxTextureSize = new int[1];
gl.glGetIntegerv(GL.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0);
Config.maxTextureSize = maxTextureSize[0];
GOut.checkerr(gl);
}
public void reshape(GLAutoDrawable d, int x, int y, int w, int h) {
}
public void displayChanged(GLAutoDrawable d, boolean cp1, boolean cp2) {
}
@Override
public void dispose(GLAutoDrawable arg0) {
// TODO Auto-generated method stub
}
});
}
public void init() {
setFocusTraversalKeysEnabled(false);
ui = new UI(null, new Coord(w, h), null);
addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent e) {
checkfs();
synchronized (events) {
events.add(e);
events.notifyAll();
}
}
public void keyPressed(KeyEvent e) {
checkfs();
synchronized (events) {
events.add(e);
events.notifyAll();
}
}
public void keyReleased(KeyEvent e) {
checkfs();
synchronized (events) {
events.add(e);
events.notifyAll();
}
}
});
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
checkfs();
synchronized (events) {
events.add(e);
events.notifyAll();
}
}
public void mouseReleased(MouseEvent e) {
checkfs();
synchronized (events) {
events.add(e);
events.notifyAll();
}
}
});
addMouseMotionListener(new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {
checkfs();
synchronized (events) {
events.add(e);
}
}
public void mouseMoved(MouseEvent e) {
checkfs();
synchronized (events) {
events.add(e);
}
}
});
addMouseWheelListener(new MouseWheelListener() {
public void mouseWheelMoved(MouseWheelEvent e) {
checkfs();
synchronized (events) {
events.add(e);
events.notifyAll();
}
}
});
inited = true;
}
private class SyncFSM implements FSMan {
private FSMan wrapped;
private boolean tgt;
private SyncFSM(FSMan wrapped) {
this.wrapped = wrapped;
tgt = wrapped.hasfs();
}
public void setfs() {
tgt = true;
}
public void setwnd() {
tgt = false;
}
public boolean hasfs() {
return (tgt);
}
private void check() {
synchronized (ui) {
if (tgt && !wrapped.hasfs())
wrapped.setfs();
if (!tgt && wrapped.hasfs())
wrapped.setwnd();
}
}
}
private void checkfs() {
if (fsm != null) {
fsm.check();
}
}
public void setfsm(FSMan fsm) {
this.fsm = new SyncFSM(fsm);
ui.fsm = this.fsm;
}
MaidUI newui(Session sess) {
Maid maid = new Maid();
ui = new MaidUI(maid, new Coord(w, h), sess);
maid.ui = (MaidUI) ui;
ui.root.gprof = prof;
ui.fsm = this.fsm;
return (MaidUI) (ui);
}
private static Cursor makeawtcurs(BufferedImage img, Coord hs) {
java.awt.Dimension cd = Toolkit.getDefaultToolkit().getBestCursorSize(img.getWidth(), img.getHeight());
BufferedImage buf = TexI.mkbuf(new Coord((int) cd.getWidth(), (int) cd.getHeight()));
java.awt.Graphics g = buf.getGraphics();
g.drawImage(img, 0, 0, null);
g.dispose();
return (Toolkit.getDefaultToolkit().createCustomCursor(buf, new java.awt.Point(hs.x, hs.y), ""));
}
void redraw(GL2 gl) {
GOut g = new GOut(gl, getContext(), MainFrame.getInnerSize());
gl.glMatrixMode(GL2.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrtho(0, getWidth(), 0, getHeight(), -1, 1);
TexRT.renderall(g);
if (curf != null)
curf.tick("texrt");
gl.glMatrixMode(GL2.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrtho(0, getWidth(), getHeight(), 0, -1, 1);
gl.glClearColor(0, 0, 0, 1);
gl.glClear(GL.GL_COLOR_BUFFER_BIT);
if (curf != null)
curf.tick("cls");
synchronized (ui) {
ui.draw(g);
}
if (curf != null)
curf.tick("draw");
if (Config.dbtext) {
g.atext("FPS: " + fps, new Coord(10, 545), 0, 1);
g.atext("Texhit: " + dth, new Coord(10, 530), 0, 1);
g.atext("Texmiss: " + dtm, new Coord(10, 515), 0, 1);
Runtime rt = Runtime.getRuntime();
long free = rt.freeMemory(), total = rt.totalMemory();
g.atext(String.format("Mem: %,011d/%,011d/%,011d/%,011d", free, total - free, total, rt.maxMemory()), new Coord(10, 500), 0, 1);
g.atext(String.format("LCache: %d/%d", Layered.cache.size(), Layered.cache.cached()), new Coord(10, 485), 0, 1);
g.atext(String.format("RT-current: %d", TexRT.current.get(gl).size()), new Coord(10, 470), 0, 1);
if (Resource.qdepth() > 0)
g.atext(String.format("RQ depth: %d (%d)", Resource.qdepth(), Resource.numloaded()), new Coord(10, 455), 0, 1);
}
Object tooltip = ui.root.tooltip(mousepos, true);
Tex tt = null;
if (tooltip != null) {
if (tooltip instanceof Text) {
tt = ((Text) tooltip).tex();
} else if (tooltip instanceof Tex) {
tt = (Tex) tooltip;
} else if (tooltip instanceof String) {
if (((String) tooltip).length() > 0)
tt = (Text.render((String) tooltip)).tex();
}
}
if (tt != null) {
Coord sz = tt.sz();
Coord pos = mousepos.add(sz.inv());
if (pos.x < 6)
pos.x = 6;
if (pos.y < 6)
pos.y = 6;
g.chcolor(244, 247, 21, 192);
g.rect(pos.add(-3, -3), sz.add(7, 6));
g.chcolor(35, 35, 35, 192);
g.frect(pos.add(-2, -2), sz.add(4, 4));
g.chcolor();
g.image(tt, pos);
}
Resource curs = ui.root.getcurs(mousepos);
if (!curs.loading) {
if (cursmode == "awt") {
if (curs != lastcursor) {
try {
setCursor(makeawtcurs(curs.layer(Resource.imgc).img, curs.layer(Resource.negc).cc));
lastcursor = curs;
} catch (Exception e) {
cursmode = "tex";
}
}
} else if (cursmode == "tex") {
Coord dc = mousepos.add(curs.layer(Resource.negc).cc.inv());
g.image(curs.layer(Resource.imgc), dc);
}
}
}
void dispatch() {
synchronized (events) {
InputEvent e = null;
while ((e = events.poll()) != null) {
if (e instanceof MouseEvent) {
MouseEvent me = (MouseEvent) e;
if (me.getID() == MouseEvent.MOUSE_PRESSED) {
ui.mousedown(me, new Coord(me.getX(), me.getY()), me.getButton());
} else if (me.getID() == MouseEvent.MOUSE_RELEASED) {
ui.mouseup(me, new Coord(me.getX(), me.getY()), me.getButton());
} else if (me.getID() == MouseEvent.MOUSE_MOVED || me.getID() == MouseEvent.MOUSE_DRAGGED) {
mousepos = new Coord(me.getX(), me.getY());
ui.mousemove(me, mousepos);
} else if (me instanceof MouseWheelEvent) {
ui.mousewheel(me, new Coord(me.getX(), me.getY()), ((MouseWheelEvent) me).getWheelRotation());
}
} else if (e instanceof KeyEvent) {
KeyEvent ke = (KeyEvent) e;
if (ke.getID() == KeyEvent.KEY_PRESSED) {
ui.keydown(ke);
} else if (ke.getID() == KeyEvent.KEY_RELEASED) {
ui.keyup(ke);
} else if (ke.getID() == KeyEvent.KEY_TYPED) {
ui.type(ke);
}
}
ui.lastevent = System.currentTimeMillis();
}
}
}
public void uglyjoglhack() throws InterruptedException {
try {
rdr = true;
display();
} catch (GLException e) {
if (e.getCause() instanceof InterruptedException) {
throw ((InterruptedException) e.getCause());
} else {
e.printStackTrace();
throw (e);
}
} finally {
rdr = false;
}
}
public void run() {
try {
long now, fthen, then;
int frames = 0;
fthen = System.currentTimeMillis();
while (true) {
then = System.currentTimeMillis();
if (Config.profile)
curf = prof.new Frame();
synchronized (ui) {
if (ui.sess != null)
ui.sess.glob.oc.ctick();
dispatch();
}
if (curf != null)
curf.tick("dsp");
uglyjoglhack();
if (curf != null)
curf.tick("aux");
frames++;
now = System.currentTimeMillis();
if (now - then < fd) {
synchronized (events) {
events.wait(fd - (now - then));
}
}
if (curf != null)
curf.tick("wait");
if (now - fthen > 1000) {
fps = frames;
frames = 0;
dth = texhit;
dtm = texmiss;
texhit = texmiss = 0;
fthen = now;
}
if (curf != null)
curf.fin();
if (Thread.interrupted())
throw (new InterruptedException());
}
} catch (InterruptedException e) {
}
}
public GraphicsConfiguration getconf() {
return (getGraphicsConfiguration());
}
}