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 solveCommand
;
32 private Color
[] colors
;
33 private JLabel statusBar
;
34 private PuzzlePanel pp
;
35 private Runtime runtime
;
36 private String
[] puzzle_args
;
37 private Graphics2D gg
;
39 private int xarg1
, xarg2
, xarg3
;
40 private int[] xPoints
, yPoints
;
41 private BufferedImage
[] blitters
= new BufferedImage
[512];
42 private ConfigDialog dlg
;
46 UIManager
.setLookAndFeel(UIManager
.getSystemLookAndFeelClassName());
47 } catch (Exception ex
) {
54 Container cp
= getContentPane();
55 cp
.setLayout(new BorderLayout());
56 runtime
= (Runtime
) Class
.forName("PuzzleEngine").newInstance();
57 runtime
.setCallJavaCB(this);
58 JMenuBar menubar
= new JMenuBar();
60 menubar
.add(jm
= new JMenu("Game"));
61 addMenuItemWithKey(jm
, "New", 'n');
62 addMenuItemCallback(jm
, "Restart", "jcallback_restart_event");
63 addMenuItemCallback(jm
, "Specific...", "jcallback_config_event", CFG_DESC
);
64 addMenuItemCallback(jm
, "Random Seed...", "jcallback_config_event", CFG_SEED
);
66 addMenuItemWithKey(jm
, "Undo", 'u');
67 addMenuItemWithKey(jm
, "Redo", 'r');
69 solveCommand
= addMenuItemCallback(jm
, "Solve", "jcallback_solve_event");
70 solveCommand
.setEnabled(false);
71 if (mainWindow
!= null) {
73 addMenuItemWithKey(jm
, "Exit", 'q');
75 menubar
.add(typeMenu
= new JMenu("Type"));
76 typeMenu
.setVisible(false);
77 menubar
.add(jm
= new JMenu("Help"));
78 addMenuItemCallback(jm
, "About", "jcallback_about_event");
80 cp
.add(pp
= new PuzzlePanel(), BorderLayout
.CENTER
);
81 pp
.addKeyListener(new KeyAdapter() {
82 public void keyPressed(KeyEvent e
) {
84 int shift
= e
.isShiftDown() ? MOD_SHFT
: 0;
85 int ctrl
= e
.isControlDown() ? MOD_CTRL
: 0;
86 switch (e
.getKeyCode()) {
87 case KeyEvent
.VK_LEFT
:
88 case KeyEvent
.VK_KP_LEFT
:
89 key
= shift
| ctrl
| CURSOR_LEFT
;
91 case KeyEvent
.VK_RIGHT
:
92 case KeyEvent
.VK_KP_RIGHT
:
93 key
= shift
| ctrl
| CURSOR_RIGHT
;
96 case KeyEvent
.VK_KP_UP
:
97 key
= shift
| ctrl
| CURSOR_UP
;
99 case KeyEvent
.VK_DOWN
:
100 case KeyEvent
.VK_KP_DOWN
:
101 key
= shift
| ctrl
| CURSOR_DOWN
;
103 case KeyEvent
.VK_PAGE_UP
:
104 key
= shift
| ctrl
| MOD_NUM_KEYPAD
| '9';
106 case KeyEvent
.VK_PAGE_DOWN
:
107 key
= shift
| ctrl
| MOD_NUM_KEYPAD
| '3';
109 case KeyEvent
.VK_HOME
:
110 key
= shift
| ctrl
| MOD_NUM_KEYPAD
| '7';
112 case KeyEvent
.VK_END
:
113 key
= shift
| ctrl
| MOD_NUM_KEYPAD
| '1';
116 if (e
.getKeyCode() >= KeyEvent
.VK_NUMPAD0
&& e
.getKeyCode() <=KeyEvent
.VK_NUMPAD9
) {
117 key
= MOD_NUM_KEYPAD
| (e
.getKeyCode() - KeyEvent
.VK_NUMPAD0
+'0');
122 runtimeCall("jcallback_key_event", new int[] {0, 0, key
});
125 public void keyTyped(KeyEvent e
) {
126 runtimeCall("jcallback_key_event", new int[] {0, 0, e
.getKeyChar()});
129 pp
.addMouseListener(new MouseAdapter() {
130 public void mouseReleased(MouseEvent e
) {
131 mousePressedReleased(e
, true);
133 public void mousePressed(MouseEvent e
) {
135 mousePressedReleased(e
, false);
137 private void mousePressedReleased(MouseEvent e
, boolean released
) {
139 if ((e
.getModifiers() & (InputEvent
.BUTTON2_MASK
| InputEvent
.SHIFT_MASK
)) != 0)
140 button
= MIDDLE_BUTTON
;
141 else if ((e
.getModifiers() & (InputEvent
.BUTTON3_MASK
| InputEvent
.ALT_MASK
)) != 0)
142 button
= RIGHT_BUTTON
;
143 else if ((e
.getModifiers() & (InputEvent
.BUTTON1_MASK
)) != 0)
144 button
= LEFT_BUTTON
;
148 button
+= LEFT_RELEASE
- LEFT_BUTTON
;
149 runtimeCall("jcallback_key_event", new int[] {e
.getX(), e
.getY(), button
});
152 pp
.addMouseMotionListener(new MouseMotionAdapter() {
153 public void mouseDragged(MouseEvent e
) {
155 if ((e
.getModifiers() & (InputEvent
.BUTTON2_MASK
| InputEvent
.SHIFT_MASK
)) != 0)
156 button
= MIDDLE_DRAG
;
157 else if ((e
.getModifiers() & (InputEvent
.BUTTON3_MASK
| InputEvent
.ALT_MASK
)) != 0)
161 runtimeCall("jcallback_key_event", new int[] {e
.getX(), e
.getY(), button
});
164 pp
.addComponentListener(new ComponentAdapter() {
165 public void componentResized(ComponentEvent e
) {
169 pp
.setFocusable(true);
171 timer
= new Timer(20, new ActionListener() {
172 public void actionPerformed(ActionEvent e
) {
173 runtimeCall("jcallback_timer_func", new int[0]);
178 gameid
= getParameter("game_id");
179 } catch (java
.lang
.NullPointerException ex
) {
182 if (gameid
== null) {
185 puzzle_args
= new String
[2];
186 puzzle_args
[0] = "puzzle";
187 puzzle_args
[1] = gameid
;
189 SwingUtilities
.invokeLater(new Runnable() {
191 runtime
.start(puzzle_args
);
195 } catch (Exception ex
) {
196 ex
.printStackTrace();
200 public void destroy() {
201 SwingUtilities
.invokeLater(new Runnable() {
204 if (mainWindow
!= null) {
205 mainWindow
.dispose();
212 protected void handleResized() {
213 pp
.createBackBuffer(pp
.getWidth(), pp
.getHeight(), colors
[0]);
214 runtimeCall("jcallback_resize", new int[] {pp
.getWidth(), pp
.getHeight()});
217 private void addMenuItemWithKey(JMenu jm
, String name
, int key
) {
218 addMenuItemCallback(jm
, name
, "jcallback_menu_key_event", key
);
221 private JMenuItem
addMenuItemCallback(JMenu jm
, String name
, final String callback
, final int arg
) {
222 return addMenuItemCallback(jm
, name
, callback
, new int[] {arg
});
225 private JMenuItem
addMenuItemCallback(JMenu jm
, String name
, final String callback
) {
226 return addMenuItemCallback(jm
, name
, callback
, new int[0]);
229 private JMenuItem
addMenuItemCallback(JMenu jm
, String name
, final String callback
, final int[] args
) {
232 typeMenu
.add(jmi
= new JCheckBoxMenuItem(name
));
234 jm
.add(jmi
= new JMenuItem(name
));
235 jmi
.addActionListener(new ActionListener() {
236 public void actionPerformed(ActionEvent e
) {
237 runtimeCall(callback
, args
);
243 protected void runtimeCall(String func
, int[] args
) {
244 if (runtimeCallWithResult(func
, args
) == 42 && mainWindow
!= null) {
249 protected int runtimeCallWithResult(String func
, int[] args
) {
251 return runtime
.call(func
, args
);
252 } catch (Runtime
.CallException ex
) {
253 ex
.printStackTrace();
258 private void buildConfigureMenuItem() {
259 if (typeMenu
.isVisible()) {
260 typeMenu
.addSeparator();
262 typeMenu
.setVisible(true);
264 addMenuItemCallback(typeMenu
, "Custom...", "jcallback_config_event", CFG_SETTINGS
);
267 private void addTypeItem(String name
, final int ptrGameParams
) {
268 typeMenu
.setVisible(true);
269 addMenuItemCallback(typeMenu
, name
, "jcallback_preset_event", ptrGameParams
);
272 public int call(int cmd
, int arg1
, int arg2
, int arg3
) {
275 case 0: // initialize
276 if (mainWindow
!= null) mainWindow
.setTitle(runtime
.cstring(arg1
));
277 if ((arg2
& 1) != 0) buildConfigureMenuItem();
278 if ((arg2
& 2) != 0) addStatusBar();
279 if ((arg2
& 4) != 0) solveCommand
.setEnabled(true);
280 colors
= new Color
[arg3
];
282 case 1: // Type menu item
283 addTypeItem(runtime
.cstring(arg1
), arg2
);
285 case 2: // MessageBox
286 JOptionPane
.showMessageDialog(this, runtime
.cstring(arg2
), runtime
.cstring(arg1
), arg3
== 0 ? JOptionPane
.INFORMATION_MESSAGE
: JOptionPane
.ERROR_MESSAGE
);
289 pp
.setPreferredSize(new Dimension(arg1
, arg2
));
290 if (mainWindow
!= null) mainWindow
.pack();
292 if (mainWindow
!= null) mainWindow
.setVisible(true);
294 case 4: // drawing tasks
297 String text
= runtime
.cstring(arg2
);
298 if (text
.equals("")) text
= " ";
299 statusBar
.setText(text
);
302 gg
= pp
.backBuffer
.createGraphics();
303 if (arg2
!= 0 || arg3
!= 0 ||
304 arg2
+ xarg2
!= getWidth() ||
305 arg3
+ xarg3
!= getHeight()) {
306 int left
= arg2
, right
= arg2
+ xarg2
;
307 int top
= arg3
, bottom
= arg3
+ xarg3
;
308 int width
= getWidth(), height
= getHeight();
309 gg
.setColor(colors
!= null ? colors
[0] : Color
.black
);
310 gg
.fillRect(0, 0, left
, height
);
311 gg
.fillRect(right
, 0, width
-right
, height
);
312 gg
.fillRect(0, 0, width
, top
);
313 gg
.fillRect(0, bottom
, width
, height
-bottom
);
314 gg
.setClip(left
, top
, right
-left
, bottom
-top
);
317 case 2: gg
.dispose(); pp
.repaint(); break;
318 case 3: gg
.setClip(arg2
, arg3
, xarg1
, xarg2
); break;
320 if (arg2
== 0 && arg3
== 0) {
321 gg
.setClip(0, 0, getWidth(), getHeight());
323 gg
.setClip(arg2
, arg3
, getWidth()-2*arg2
, getHeight()-2*arg3
);
327 gg
.setColor(colors
[xarg3
]);
328 gg
.fillRect(arg2
, arg3
, xarg1
, xarg2
);
331 gg
.setColor(colors
[xarg3
]);
332 gg
.drawLine(arg2
, arg3
, xarg1
, xarg2
);
335 xPoints
= new int[arg2
];
336 yPoints
= new int[arg2
];
340 gg
.setColor(colors
[arg3
]);
341 gg
.fillPolygon(xPoints
, yPoints
, xPoints
.length
);
343 gg
.setColor(colors
[arg2
]);
344 gg
.drawPolygon(xPoints
, yPoints
, xPoints
.length
);
348 gg
.setColor(colors
[arg3
]);
349 gg
.fillOval(xarg1
-xarg3
, xarg2
-xarg3
, xarg3
*2, xarg3
*2);
351 gg
.setColor(colors
[arg2
]);
352 gg
.drawOval(xarg1
-xarg3
, xarg2
-xarg3
, xarg3
*2, xarg3
*2);
355 for(int i
=0; i
<blitters
.length
; i
++) {
356 if (blitters
[i
] == null) {
357 blitters
[i
] = new BufferedImage(arg2
, arg3
, BufferedImage
.TYPE_3BYTE_BGR
);
361 throw new RuntimeException("No free blitter found!");
362 case 11: blitters
[arg2
] = null; break;
364 timer
.start(); break;
369 case 5: // more arguments
374 case 6: // polygon vertex
379 gg
.setColor(colors
[arg2
]);
381 String text
= runtime
.utfstring(arg3
);
382 Font ft
= new Font((xarg3
& 0x10) != 0 ?
"Monospaced" : "Dialog",
384 int height100
= this.getFontMetrics(ft
).getHeight();
385 ft
= ft
.deriveFont(arg1
* 100 / (float)height100
);
386 FontMetrics fm
= this.getFontMetrics(ft
);
387 int asc
= fm
.getAscent(), desc
= fm
.getDescent();
388 if ((xarg3
& ALIGN_VCENTRE
) != 0)
389 xarg2
+= asc
- (asc
+desc
)/2;
390 int wid
= fm
.stringWidth(text
);
391 if ((xarg3
& ALIGN_HCENTRE
) != 0)
393 else if ((xarg3
& ALIGN_HRIGHT
) != 0)
396 gg
.drawString(text
, xarg1
, xarg2
);
399 case 8: // blitter_save
400 Graphics g2
= blitters
[arg1
].createGraphics();
401 g2
.drawImage(pp
.backBuffer
, 0, 0, blitters
[arg1
].getWidth(), blitters
[arg1
].getHeight(),
402 arg2
, arg3
, arg2
+ blitters
[arg1
].getWidth(), arg3
+ blitters
[arg1
].getHeight(), this);
405 case 9: // blitter_load
406 gg
.drawImage(blitters
[arg1
], arg2
, arg3
, this);
408 case 10: // dialog_init
409 dlg
= new ConfigDialog(this, runtime
.cstring(arg1
));
411 case 11: // dialog_add_control
417 String name
= runtime
.cstring(xarg3
);
420 dlg
.addTextBox(ptr
, name
, runtime
.cstring(sval_ptr
));
423 dlg
.addCheckBox(ptr
, name
, ival
!= 0);
426 dlg
.addComboBox(ptr
, name
, runtime
.cstring(sval_ptr
), ival
);
434 case 13: // tick a menu item
435 if (arg1
< 0) arg1
= typeMenu
.getItemCount() - 1;
436 for (int i
= 0; i
< typeMenu
.getItemCount(); i
++) {
437 if (typeMenu
.getMenuComponent(i
) instanceof JCheckBoxMenuItem
) {
438 ((JCheckBoxMenuItem
)typeMenu
.getMenuComponent(i
)).setSelected(arg1
== i
);
443 if (cmd
>= 1024 && cmd
< 2048) { // palette
444 colors
[cmd
-1024] = new Color(arg1
, arg2
, arg3
);
447 pp
.setBackground(colors
[0]);
448 if (statusBar
!= null) statusBar
.setBackground(colors
[0]);
449 this.setBackground(colors
[0]);
453 } catch (Throwable ex
) {
454 ex
.printStackTrace();
460 private void addStatusBar() {
461 statusBar
= new JLabel("test");
462 statusBar
.setBorder(new BevelBorder(BevelBorder
.LOWERED
));
463 getContentPane().add(BorderLayout
.SOUTH
,statusBar
);
467 public static void main(String
[] args
) {
468 final PuzzleApplet a
= new PuzzleApplet();
469 JFrame jf
= new JFrame("Loading...");
470 jf
.getContentPane().setLayout(new BorderLayout());
471 jf
.getContentPane().add(a
, BorderLayout
.CENTER
);
475 jf
.setDefaultCloseOperation(JFrame
.EXIT_ON_CLOSE
);
476 jf
.addWindowListener(new WindowAdapter() {
477 public void windowClosing(WindowEvent e
) {
485 public static class PuzzlePanel
extends JPanel
{
487 private static final long serialVersionUID
= 1L;
488 protected BufferedImage backBuffer
;
490 public PuzzlePanel() {
491 setPreferredSize(new Dimension(100,100));
492 createBackBuffer(100,100, Color
.black
);
495 public void createBackBuffer(int w
, int h
, Color bg
) {
496 if (w
> 0 && h
> 0) {
497 backBuffer
= new BufferedImage(w
,h
, BufferedImage
.TYPE_3BYTE_BGR
);
498 Graphics g
= backBuffer
.createGraphics();
500 g
.fillRect(0, 0, w
, h
);
505 protected void paintComponent(Graphics g
) {
506 g
.drawImage(backBuffer
, 0, 0, this);
510 public static class ConfigComponent
{
512 public int configItemPointer
;
513 public JComponent component
;
515 public ConfigComponent(int type
, int configItemPointer
, JComponent component
) {
517 this.configItemPointer
= configItemPointer
;
518 this.component
= component
;
522 public class ConfigDialog
extends JDialog
{
524 private GridBagConstraints gbcLeft
= new GridBagConstraints(
525 GridBagConstraints
.RELATIVE
, GridBagConstraints
.RELATIVE
, 1, 1,
526 0, 0, GridBagConstraints
.WEST
, GridBagConstraints
.NONE
,
527 new Insets(0, 0, 0, 0), 0, 0);
528 private GridBagConstraints gbcRight
= new GridBagConstraints(
529 GridBagConstraints
.RELATIVE
, GridBagConstraints
.RELATIVE
,
530 GridBagConstraints
.REMAINDER
, 1, 1.0, 0,
531 GridBagConstraints
.CENTER
, GridBagConstraints
.HORIZONTAL
,
532 new Insets(5, 5, 5, 5), 0, 0);
533 private GridBagConstraints gbcBottom
= new GridBagConstraints(
534 GridBagConstraints
.RELATIVE
, GridBagConstraints
.RELATIVE
,
535 GridBagConstraints
.REMAINDER
, GridBagConstraints
.REMAINDER
,
536 1.0, 1.0, GridBagConstraints
.CENTER
,
537 GridBagConstraints
.HORIZONTAL
, new Insets(5, 5, 5, 5), 0, 0);
539 private static final long serialVersionUID
= 1L;
540 private List components
= new ArrayList();
542 public ConfigDialog(JApplet parent
, String title
) {
543 super(JOptionPane
.getFrameForComponent(parent
), title
, true);
544 getContentPane().setLayout(new GridBagLayout());
547 public void addTextBox(int ptr
, String name
, String value
) {
548 getContentPane().add(new JLabel(name
), gbcLeft
);
549 JComponent c
= new JTextField(value
, 25);
550 getContentPane().add(c
, gbcRight
);
551 components
.add(new ConfigComponent(C_STRING
, ptr
, c
));
555 public void addCheckBox(int ptr
, String name
, boolean selected
) {
556 JComponent c
= new JCheckBox(name
, selected
);
557 getContentPane().add(c
, gbcRight
);
558 components
.add(new ConfigComponent(C_BOOLEAN
, ptr
, c
));
561 public void addComboBox(int ptr
, String name
, String values
, int selected
) {
562 getContentPane().add(new JLabel(name
), gbcLeft
);
563 StringTokenizer st
= new StringTokenizer(values
.substring(1), values
.substring(0,1));
564 JComboBox c
= new JComboBox();
565 c
.setEditable(false);
566 while(st
.hasMoreTokens())
567 c
.addItem(st
.nextToken());
568 c
.setSelectedIndex(selected
);
569 getContentPane().add(c
, gbcRight
);
570 components
.add(new ConfigComponent(C_CHOICES
, ptr
, c
));
573 public void finish() {
574 JPanel buttons
= new JPanel(new GridLayout(1, 2, 5, 5));
575 getContentPane().add(buttons
, gbcBottom
);
577 buttons
.add(b
=new JButton("OK"));
578 b
.addActionListener(new ActionListener() {
579 public void actionPerformed(ActionEvent e
) {
584 getRootPane().setDefaultButton(b
);
585 buttons
.add(b
=new JButton("Cancel"));
586 b
.addActionListener(new ActionListener() {
587 public void actionPerformed(ActionEvent e
) {
591 setDefaultCloseOperation(DISPOSE_ON_CLOSE
);
593 setLocationRelativeTo(null);
596 private void save() {
597 for (int i
= 0; i
< components
.size(); i
++) {
598 ConfigComponent cc
= (ConfigComponent
) components
.get(i
);
601 JTextField jtf
= (JTextField
)cc
.component
;
602 runtimeCall("jcallback_config_set_string", new int[] {cc
.configItemPointer
, runtime
.strdup(jtf
.getText())});
605 JCheckBox jcb
= (JCheckBox
)cc
.component
;
606 runtimeCall("jcallback_config_set_boolean", new int[] {cc
.configItemPointer
, jcb
.isSelected()?
1:0});
609 JComboBox jcm
= (JComboBox
)cc
.component
;
610 runtimeCall("jcallback_config_set_choice", new int[] {cc
.configItemPointer
, jcm
.getSelectedIndex()});
614 runtimeCall("jcallback_config_ok", new int[0]);