2 * PuzzleApplet.java: NestedVM applet for the puzzle collection
5 import java.awt.event.*;
6 import java.awt.image.BufferedImage;
9 import javax.swing.border.BevelBorder;
10 import javax.swing.Timer;
11 import java.util.List;
13 import org.ibex.nestedvm.Runtime;
15 public class PuzzleApplet extends JApplet implements Runtime.CallJavaCB {
17 private static final long serialVersionUID = 1L;
19 private static final int CFG_SETTINGS = 0, CFG_SEED = 1, CFG_DESC = 2,
20 LEFT_BUTTON = 0x0200, MIDDLE_BUTTON = 0x201, RIGHT_BUTTON = 0x202,
21 LEFT_DRAG = 0x203, MIDDLE_DRAG = 0x204, RIGHT_DRAG = 0x205,
22 LEFT_RELEASE = 0x206, CURSOR_UP = 0x209, CURSOR_DOWN = 0x20a,
23 CURSOR_LEFT = 0x20b, CURSOR_RIGHT = 0x20c, MOD_CTRL = 0x1000,
24 MOD_SHFT = 0x2000, MOD_NUM_KEYPAD = 0x4000, ALIGN_VCENTRE = 0x100,
25 ALIGN_HCENTRE = 0x001, ALIGN_HRIGHT = 0x002, C_STRING = 0,
26 C_CHOICES = 1, C_BOOLEAN = 2;
28 private JFrame mainWindow;
30 private JMenu typeMenu;
31 private JMenuItem[] typeMenuItems;
32 private int customMenuItemIndex;
34 private JMenuItem solveCommand;
35 private Color[] colors;
36 private JLabel statusBar;
37 private PuzzlePanel pp;
38 private Runtime runtime;
39 private String[] puzzle_args;
40 private Graphics2D gg;
42 private int xarg1, xarg2, xarg3;
43 private int[] xPoints, yPoints;
44 private BufferedImage[] blitters = new BufferedImage[512];
45 private ConfigDialog dlg;
49 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
50 } catch (Exception ex) {
57 Container cp = getContentPane();
58 cp.setLayout(new BorderLayout());
59 runtime = (Runtime) Class.forName("PuzzleEngine").newInstance();
60 runtime.setCallJavaCB(this);
61 JMenuBar menubar = new JMenuBar();
63 menubar.add(jm = new JMenu("Game"));
64 addMenuItemCallback(jm, "New", "jcallback_newgame_event");
65 addMenuItemCallback(jm, "Restart", "jcallback_restart_event");
66 addMenuItemCallback(jm, "Specific...", "jcallback_config_event", CFG_DESC);
67 addMenuItemCallback(jm, "Random Seed...", "jcallback_config_event", CFG_SEED);
69 addMenuItemCallback(jm, "Undo", "jcallback_undo_event");
70 addMenuItemCallback(jm, "Redo", "jcallback_redo_event");
72 solveCommand = addMenuItemCallback(jm, "Solve", "jcallback_solve_event");
73 solveCommand.setEnabled(false);
74 if (mainWindow != null) {
76 addMenuItemCallback(jm, "Exit", "jcallback_quit_event");
78 menubar.add(typeMenu = new JMenu("Type"));
79 typeMenu.setVisible(false);
80 menubar.add(jm = new JMenu("Help"));
81 addMenuItemCallback(jm, "About", "jcallback_about_event");
83 cp.add(pp = new PuzzlePanel(), BorderLayout.CENTER);
84 pp.addKeyListener(new KeyAdapter() {
85 public void keyPressed(KeyEvent e) {
87 int shift = e.isShiftDown() ? MOD_SHFT : 0;
88 int ctrl = e.isControlDown() ? MOD_CTRL : 0;
89 switch (e.getKeyCode()) {
90 case KeyEvent.VK_LEFT:
91 case KeyEvent.VK_KP_LEFT:
92 key = shift | ctrl | CURSOR_LEFT;
94 case KeyEvent.VK_RIGHT:
95 case KeyEvent.VK_KP_RIGHT:
96 key = shift | ctrl | CURSOR_RIGHT;
99 case KeyEvent.VK_KP_UP:
100 key = shift | ctrl | CURSOR_UP;
102 case KeyEvent.VK_DOWN:
103 case KeyEvent.VK_KP_DOWN:
104 key = shift | ctrl | CURSOR_DOWN;
106 case KeyEvent.VK_PAGE_UP:
107 key = shift | ctrl | MOD_NUM_KEYPAD | '9';
109 case KeyEvent.VK_PAGE_DOWN:
110 key = shift | ctrl | MOD_NUM_KEYPAD | '3';
112 case KeyEvent.VK_HOME:
113 key = shift | ctrl | MOD_NUM_KEYPAD | '7';
115 case KeyEvent.VK_END:
116 key = shift | ctrl | MOD_NUM_KEYPAD | '1';
119 if (e.getKeyCode() >= KeyEvent.VK_NUMPAD0 && e.getKeyCode() <=KeyEvent.VK_NUMPAD9) {
120 key = MOD_NUM_KEYPAD | (e.getKeyCode() - KeyEvent.VK_NUMPAD0+'0');
125 runtimeCall("jcallback_key_event", new int[] {0, 0, key});
128 public void keyTyped(KeyEvent e) {
129 int key = e.getKeyChar();
130 if (key == 26 && e.isShiftDown() && e.isControlDown()) {
131 runtimeCall("jcallback_redo_event", new int[0]);
134 runtimeCall("jcallback_key_event", new int[] {0, 0, key});
137 pp.addMouseListener(new MouseAdapter() {
138 public void mouseReleased(MouseEvent e) {
139 mousePressedReleased(e, true);
141 public void mousePressed(MouseEvent e) {
143 mousePressedReleased(e, false);
145 private void mousePressedReleased(MouseEvent e, boolean released) {
147 if ((e.getModifiers() & (InputEvent.BUTTON2_MASK | InputEvent.SHIFT_MASK)) != 0)
148 button = MIDDLE_BUTTON;
149 else if ((e.getModifiers() & (InputEvent.BUTTON3_MASK | InputEvent.ALT_MASK)) != 0)
150 button = RIGHT_BUTTON;
151 else if ((e.getModifiers() & (InputEvent.BUTTON1_MASK)) != 0)
152 button = LEFT_BUTTON;
156 button += LEFT_RELEASE - LEFT_BUTTON;
157 runtimeCall("jcallback_key_event", new int[] {e.getX(), e.getY(), button});
160 pp.addMouseMotionListener(new MouseMotionAdapter() {
161 public void mouseDragged(MouseEvent e) {
163 if ((e.getModifiers() & (InputEvent.BUTTON2_MASK | InputEvent.SHIFT_MASK)) != 0)
164 button = MIDDLE_DRAG;
165 else if ((e.getModifiers() & (InputEvent.BUTTON3_MASK | InputEvent.ALT_MASK)) != 0)
169 runtimeCall("jcallback_key_event", new int[] {e.getX(), e.getY(), button});
172 pp.addComponentListener(new ComponentAdapter() {
173 public void componentResized(ComponentEvent e) {
177 pp.setFocusable(true);
179 timer = new Timer(20, new ActionListener() {
180 public void actionPerformed(ActionEvent e) {
181 runtimeCall("jcallback_timer_func", new int[0]);
186 gameid = getParameter("game_id");
187 } catch (java.lang.NullPointerException ex) {
190 if (gameid == null) {
193 puzzle_args = new String[2];
194 puzzle_args[0] = "puzzle";
195 puzzle_args[1] = gameid;
197 SwingUtilities.invokeLater(new Runnable() {
199 runtime.start(puzzle_args);
203 } catch (Exception ex) {
204 ex.printStackTrace();
208 public void destroy() {
209 SwingUtilities.invokeLater(new Runnable() {
212 if (mainWindow != null) {
213 mainWindow.dispose();
220 protected void handleResized() {
221 pp.createBackBuffer(pp.getWidth(), pp.getHeight(), colors[0]);
222 runtimeCall("jcallback_resize", new int[] {pp.getWidth(), pp.getHeight()});
225 private JMenuItem addMenuItemCallback(JMenu jm, String name, final String callback, final int arg) {
226 return addMenuItemCallback(jm, name, callback, new int[] {arg}, false);
229 private JMenuItem addMenuItemCallback(JMenu jm, String name, final String callback) {
230 return addMenuItemCallback(jm, name, callback, new int[0], false);
233 private JMenuItem addMenuItemCallback(JMenu jm, String name, final String callback, final int[] args, boolean checkbox) {
236 jm.add(jmi = new JCheckBoxMenuItem(name));
238 jm.add(jmi = new JMenuItem(name));
239 jmi.addActionListener(new ActionListener() {
240 public void actionPerformed(ActionEvent e) {
241 runtimeCall(callback, args);
247 protected void runtimeCall(String func, int[] args) {
248 if (runtimeCallWithResult(func, args) == 42 && mainWindow != null) {
253 protected int runtimeCallWithResult(String func, int[] args) {
255 return runtime.call(func, args);
256 } catch (Runtime.CallException ex) {
257 ex.printStackTrace();
262 private void buildConfigureMenuItem() {
263 if (typeMenu.isVisible()) {
264 typeMenu.addSeparator();
266 typeMenu.setVisible(true);
268 typeMenuItems[customMenuItemIndex] =
269 addMenuItemCallback(typeMenu, "Custom...",
270 "jcallback_config_event",
271 new int[] {CFG_SETTINGS}, true);
274 private void addTypeItem
275 (JMenu targetMenu, String name, int newId, final int ptrGameParams) {
277 typeMenu.setVisible(true);
278 typeMenuItems[newId] =
279 addMenuItemCallback(targetMenu, name,
280 "jcallback_preset_event",
281 new int[] {ptrGameParams}, true);
284 private void addTypeSubmenu
285 (JMenu targetMenu, String name, int newId) {
287 JMenu newMenu = new JMenu(name);
288 newMenu.setVisible(true);
289 typeMenuItems[newId] = newMenu;
290 targetMenu.add(newMenu);
293 public int call(int cmd, int arg1, int arg2, int arg3) {
296 case 0: // initialize
297 if (mainWindow != null) mainWindow.setTitle(runtime.cstring(arg1));
298 if ((arg2 & 1) != 0) buildConfigureMenuItem();
299 if ((arg2 & 2) != 0) addStatusBar();
300 if ((arg2 & 4) != 0) solveCommand.setEnabled(true);
301 colors = new Color[arg3];
303 case 1: // configure Type menu
306 typeMenuItems = new JMenuItem[arg2 + 2];
307 typeMenuItems[arg2] = typeMenu;
308 customMenuItemIndex = arg2 + 1;
310 } else if (xarg1 != 0) {
311 addTypeItem((JMenu)typeMenuItems[arg2],
312 runtime.cstring(arg1), arg3, xarg1);
314 addTypeSubmenu((JMenu)typeMenuItems[arg2],
315 runtime.cstring(arg1), arg3);
318 case 2: // MessageBox
319 JOptionPane.showMessageDialog(this, runtime.cstring(arg2), runtime.cstring(arg1), arg3 == 0 ? JOptionPane.INFORMATION_MESSAGE : JOptionPane.ERROR_MESSAGE);
322 pp.setPreferredSize(new Dimension(arg1, arg2));
323 if (mainWindow != null) mainWindow.pack();
325 if (mainWindow != null) mainWindow.setVisible(true);
327 case 4: // drawing tasks
330 String text = runtime.cstring(arg2);
331 if (text.equals("")) text = " ";
332 statusBar.setText(text);
335 gg = pp.backBuffer.createGraphics();
336 if (arg2 != 0 || arg3 != 0 ||
337 arg2 + xarg2 != getWidth() ||
338 arg3 + xarg3 != getHeight()) {
339 int left = arg2, right = arg2 + xarg2;
340 int top = arg3, bottom = arg3 + xarg3;
341 int width = getWidth(), height = getHeight();
342 gg.setColor(colors != null ? colors[0] : Color.black);
343 gg.fillRect(0, 0, left, height);
344 gg.fillRect(right, 0, width-right, height);
345 gg.fillRect(0, 0, width, top);
346 gg.fillRect(0, bottom, width, height-bottom);
347 gg.setClip(left, top, right-left, bottom-top);
350 case 2: gg.dispose(); pp.repaint(); break;
351 case 3: gg.setClip(arg2, arg3, xarg1, xarg2); break;
353 if (arg2 == 0 && arg3 == 0) {
354 gg.setClip(0, 0, getWidth(), getHeight());
356 gg.setClip(arg2, arg3, getWidth()-2*arg2, getHeight()-2*arg3);
360 gg.setColor(colors[xarg3]);
361 gg.fillRect(arg2, arg3, xarg1, xarg2);
364 gg.setColor(colors[xarg3]);
365 gg.drawLine(arg2, arg3, xarg1, xarg2);
368 xPoints = new int[arg2];
369 yPoints = new int[arg2];
373 gg.setColor(colors[arg3]);
374 gg.fillPolygon(xPoints, yPoints, xPoints.length);
376 gg.setColor(colors[arg2]);
377 gg.drawPolygon(xPoints, yPoints, xPoints.length);
381 gg.setColor(colors[arg3]);
382 gg.fillOval(xarg1-xarg3, xarg2-xarg3, xarg3*2, xarg3*2);
384 gg.setColor(colors[arg2]);
385 gg.drawOval(xarg1-xarg3, xarg2-xarg3, xarg3*2, xarg3*2);
388 for(int i=0; i<blitters.length; i++) {
389 if (blitters[i] == null) {
390 blitters[i] = new BufferedImage(arg2, arg3, BufferedImage.TYPE_3BYTE_BGR);
394 throw new RuntimeException("No free blitter found!");
395 case 11: blitters[arg2] = null; break;
397 timer.start(); break;
402 case 5: // more arguments
407 case 6: // polygon vertex
412 gg.setColor(colors[arg2]);
414 String text = runtime.utfstring(arg3);
415 Font ft = new Font((xarg3 & 0x10) != 0 ? "Monospaced" : "Dialog",
417 int height100 = this.getFontMetrics(ft).getHeight();
418 ft = ft.deriveFont(arg1 * 100 / (float)height100);
419 FontMetrics fm = this.getFontMetrics(ft);
420 int asc = fm.getAscent(), desc = fm.getDescent();
421 if ((xarg3 & ALIGN_VCENTRE) != 0)
422 xarg2 += asc - (asc+desc)/2;
423 int wid = fm.stringWidth(text);
424 if ((xarg3 & ALIGN_HCENTRE) != 0)
426 else if ((xarg3 & ALIGN_HRIGHT) != 0)
429 gg.drawString(text, xarg1, xarg2);
432 case 8: // blitter_save
433 Graphics g2 = blitters[arg1].createGraphics();
434 g2.drawImage(pp.backBuffer, 0, 0, blitters[arg1].getWidth(), blitters[arg1].getHeight(),
435 arg2, arg3, arg2 + blitters[arg1].getWidth(), arg3 + blitters[arg1].getHeight(), this);
438 case 9: // blitter_load
439 gg.drawImage(blitters[arg1], arg2, arg3, this);
441 case 10: // dialog_init
442 dlg= new ConfigDialog(this, runtime.cstring(arg1));
444 case 11: // dialog_add_control
450 String name = runtime.cstring(xarg3);
453 dlg.addTextBox(ptr, name, runtime.cstring(sval_ptr));
456 dlg.addCheckBox(ptr, name, ival != 0);
459 dlg.addComboBox(ptr, name, runtime.cstring(sval_ptr), ival);
467 case 13: // tick a menu item
468 if (arg1 < 0) arg1 = customMenuItemIndex;
469 for (int i = 0; i < typeMenuItems.length; i++) {
470 if (typeMenuItems[i] instanceof JCheckBoxMenuItem) {
471 ((JCheckBoxMenuItem)typeMenuItems[i]).setSelected
477 if (cmd >= 1024 && cmd < 2048) { // palette
478 colors[cmd-1024] = new Color(arg1, arg2, arg3);
481 pp.setBackground(colors[0]);
482 if (statusBar != null) statusBar.setBackground(colors[0]);
483 this.setBackground(colors[0]);
487 } catch (Throwable ex) {
488 ex.printStackTrace();
494 private void addStatusBar() {
495 statusBar = new JLabel("test");
496 statusBar.setBorder(new BevelBorder(BevelBorder.LOWERED));
497 getContentPane().add(BorderLayout.SOUTH,statusBar);
501 public static void main(String[] args) {
502 final PuzzleApplet a = new PuzzleApplet();
503 JFrame jf = new JFrame("Loading...");
504 jf.getContentPane().setLayout(new BorderLayout());
505 jf.getContentPane().add(a, BorderLayout.CENTER);
509 jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
510 jf.addWindowListener(new WindowAdapter() {
511 public void windowClosing(WindowEvent e) {
519 public static class PuzzlePanel extends JPanel {
521 private static final long serialVersionUID = 1L;
522 protected BufferedImage backBuffer;
524 public PuzzlePanel() {
525 setPreferredSize(new Dimension(100,100));
526 createBackBuffer(100,100, Color.black);
529 public void createBackBuffer(int w, int h, Color bg) {
530 if (w > 0 && h > 0) {
531 backBuffer = new BufferedImage(w,h, BufferedImage.TYPE_3BYTE_BGR);
532 Graphics g = backBuffer.createGraphics();
534 g.fillRect(0, 0, w, h);
539 protected void paintComponent(Graphics g) {
540 g.drawImage(backBuffer, 0, 0, this);
544 public static class ConfigComponent {
546 public int configItemPointer;
547 public JComponent component;
549 public ConfigComponent(int type, int configItemPointer, JComponent component) {
551 this.configItemPointer = configItemPointer;
552 this.component = component;
556 public class ConfigDialog extends JDialog {
558 private GridBagConstraints gbcLeft = new GridBagConstraints(
559 GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE, 1, 1,
560 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE,
561 new Insets(0, 0, 0, 0), 0, 0);
562 private GridBagConstraints gbcRight = new GridBagConstraints(
563 GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE,
564 GridBagConstraints.REMAINDER, 1, 1.0, 0,
565 GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
566 new Insets(5, 5, 5, 5), 0, 0);
567 private GridBagConstraints gbcBottom = new GridBagConstraints(
568 GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE,
569 GridBagConstraints.REMAINDER, GridBagConstraints.REMAINDER,
570 1.0, 1.0, GridBagConstraints.CENTER,
571 GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 0, 0);
573 private static final long serialVersionUID = 1L;
574 private List components = new ArrayList();
576 public ConfigDialog(JApplet parent, String title) {
577 super(JOptionPane.getFrameForComponent(parent), title, true);
578 getContentPane().setLayout(new GridBagLayout());
581 public void addTextBox(int ptr, String name, String value) {
582 getContentPane().add(new JLabel(name), gbcLeft);
583 JComponent c = new JTextField(value, 25);
584 getContentPane().add(c, gbcRight);
585 components.add(new ConfigComponent(C_STRING, ptr, c));
589 public void addCheckBox(int ptr, String name, boolean selected) {
590 JComponent c = new JCheckBox(name, selected);
591 getContentPane().add(c, gbcRight);
592 components.add(new ConfigComponent(C_BOOLEAN, ptr, c));
595 public void addComboBox(int ptr, String name, String values, int selected) {
596 getContentPane().add(new JLabel(name), gbcLeft);
597 StringTokenizer st = new StringTokenizer(values.substring(1), values.substring(0,1));
598 JComboBox c = new JComboBox();
599 c.setEditable(false);
600 while(st.hasMoreTokens())
601 c.addItem(st.nextToken());
602 c.setSelectedIndex(selected);
603 getContentPane().add(c, gbcRight);
604 components.add(new ConfigComponent(C_CHOICES, ptr, c));
607 public void finish() {
608 JPanel buttons = new JPanel(new GridLayout(1, 2, 5, 5));
609 getContentPane().add(buttons, gbcBottom);
611 buttons.add(b=new JButton("OK"));
612 b.addActionListener(new ActionListener() {
613 public void actionPerformed(ActionEvent e) {
618 getRootPane().setDefaultButton(b);
619 buttons.add(b=new JButton("Cancel"));
620 b.addActionListener(new ActionListener() {
621 public void actionPerformed(ActionEvent e) {
625 setDefaultCloseOperation(DISPOSE_ON_CLOSE);
627 setLocationRelativeTo(null);
630 private void save() {
631 for (int i = 0; i < components.size(); i++) {
632 ConfigComponent cc = (ConfigComponent) components.get(i);
635 JTextField jtf = (JTextField)cc.component;
636 runtimeCall("jcallback_config_set_string", new int[] {cc.configItemPointer, runtime.strdup(jtf.getText())});
639 JCheckBox jcb = (JCheckBox)cc.component;
640 runtimeCall("jcallback_config_set_boolean", new int[] {cc.configItemPointer, jcb.isSelected()?1:0});
643 JComboBox jcm = (JComboBox)cc.component;
644 runtimeCall("jcallback_config_set_choice", new int[] {cc.configItemPointer, jcm.getSelectedIndex()});
648 runtimeCall("jcallback_config_ok", new int[0]);