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 Graphics2D gg
;
38 private int xarg1
, xarg2
, xarg3
;
39 private int[] xPoints
, yPoints
;
40 private BufferedImage
[] blitters
= new BufferedImage
[512];
41 private ConfigDialog dlg
;
45 UIManager
.setLookAndFeel(UIManager
.getSystemLookAndFeelClassName());
46 } catch (Exception ex
) {
53 Container cp
= getContentPane();
54 cp
.setLayout(new BorderLayout());
55 runtime
= (Runtime
) Class
.forName("PuzzleEngine").newInstance();
56 runtime
.setCallJavaCB(this);
57 JMenuBar menubar
= new JMenuBar();
59 menubar
.add(jm
= new JMenu("Game"));
60 addMenuItemWithKey(jm
, "New", 'n');
61 addMenuItemCallback(jm
, "Restart", "jcallback_restart_event");
62 addMenuItemCallback(jm
, "Specific...", "jcallback_config_event", CFG_DESC
);
63 addMenuItemCallback(jm
, "Random Seed...", "jcallback_config_event", CFG_SEED
);
65 addMenuItemWithKey(jm
, "Undo", 'u');
66 addMenuItemWithKey(jm
, "Redo", 'r');
68 solveCommand
= addMenuItemCallback(jm
, "Solve", "jcallback_solve_event");
69 solveCommand
.setEnabled(false
);
70 if (mainWindow
!= null
) {
72 addMenuItemWithKey(jm
, "Exit", 'q');
74 menubar
.add(typeMenu
= new JMenu("Type"));
75 typeMenu
.setVisible(false
);
76 menubar
.add(jm
= new JMenu("Help"));
77 addMenuItemCallback(jm
, "About", "jcallback_about_event");
79 cp
.add(pp
= new PuzzlePanel(), BorderLayout
.CENTER
);
80 pp
.addKeyListener(new KeyAdapter() {
81 public void keyPressed(KeyEvent e
) {
83 int shift
= e
.isShiftDown() ? MOD_SHFT
: 0;
84 int ctrl
= e
.isControlDown() ? MOD_CTRL
: 0;
85 switch (e
.getKeyCode()) {
86 case KeyEvent
.VK_LEFT
:
87 case KeyEvent
.VK_KP_LEFT
:
88 key
= shift
| ctrl
| CURSOR_LEFT
;
90 case KeyEvent
.VK_RIGHT
:
91 case KeyEvent
.VK_KP_RIGHT
:
92 key
= shift
| ctrl
| CURSOR_RIGHT
;
95 case KeyEvent
.VK_KP_UP
:
96 key
= shift
| ctrl
| CURSOR_UP
;
98 case KeyEvent
.VK_DOWN
:
99 case KeyEvent
.VK_KP_DOWN
:
100 key
= shift
| ctrl
| CURSOR_DOWN
;
102 case KeyEvent
.VK_PAGE_UP
:
103 key
= shift
| ctrl
| MOD_NUM_KEYPAD
| '9';
105 case KeyEvent
.VK_PAGE_DOWN
:
106 key
= shift
| ctrl
| MOD_NUM_KEYPAD
| '3';
108 case KeyEvent
.VK_HOME
:
109 key
= shift
| ctrl
| MOD_NUM_KEYPAD
| '7';
111 case KeyEvent
.VK_END
:
112 key
= shift
| ctrl
| MOD_NUM_KEYPAD
| '1';
115 if (e
.getKeyCode() >= KeyEvent
.VK_NUMPAD0
&& e
.getKeyCode() <=KeyEvent
.VK_NUMPAD9
) {
116 key
= MOD_NUM_KEYPAD
| (e
.getKeyCode() - KeyEvent
.VK_NUMPAD0
+'0');
121 runtimeCall("jcallback_key_event", new int[] {0, 0, key
});
124 public void keyTyped(KeyEvent e
) {
125 runtimeCall("jcallback_key_event", new int[] {0, 0, e
.getKeyChar()});
128 pp
.addMouseListener(new MouseAdapter() {
129 public void mouseReleased(MouseEvent e
) {
130 mousePressedReleased(e
, true
);
132 public void mousePressed(MouseEvent e
) {
134 mousePressedReleased(e
, false
);
136 private void mousePressedReleased(MouseEvent e
, boolean released
) {
138 if ((e
.getModifiers() & (InputEvent
.BUTTON2_MASK
| InputEvent
.SHIFT_MASK
)) != 0)
139 button
= MIDDLE_BUTTON
;
140 else if ((e
.getModifiers() & (InputEvent
.BUTTON3_MASK
| InputEvent
.ALT_MASK
)) != 0)
141 button
= RIGHT_BUTTON
;
142 else if ((e
.getModifiers() & (InputEvent
.BUTTON1_MASK
)) != 0)
143 button
= LEFT_BUTTON
;
147 button
+= LEFT_RELEASE
- LEFT_BUTTON
;
148 runtimeCall("jcallback_key_event", new int[] {e
.getX(), e
.getY(), button
});
151 pp
.addMouseMotionListener(new MouseMotionAdapter() {
152 public void mouseDragged(MouseEvent e
) {
154 if ((e
.getModifiers() & (InputEvent
.BUTTON2_MASK
| InputEvent
.SHIFT_MASK
)) != 0)
155 button
= MIDDLE_DRAG
;
156 else if ((e
.getModifiers() & (InputEvent
.BUTTON3_MASK
| InputEvent
.ALT_MASK
)) != 0)
160 runtimeCall("jcallback_key_event", new int[] {e
.getX(), e
.getY(), button
});
163 pp
.addComponentListener(new ComponentAdapter() {
164 public void componentResized(ComponentEvent e
) {
168 pp
.setFocusable(true
);
170 timer
= new Timer(20, new ActionListener() {
171 public void actionPerformed(ActionEvent e
) {
172 runtimeCall("jcallback_timer_func", new int[0]);
175 SwingUtilities
.invokeLater(new Runnable() {
181 } catch (Exception ex
) {
182 ex
.printStackTrace();
186 public void destroy() {
187 SwingUtilities
.invokeLater(new Runnable() {
190 if (mainWindow
!= null
) {
191 mainWindow
.dispose();
198 protected void handleResized() {
199 pp
.createBackBuffer(pp
.getWidth(), pp
.getHeight(), colors
[0]);
200 runtimeCall("jcallback_resize", new int[] {pp
.getWidth(), pp
.getHeight()});
203 private void addMenuItemWithKey(JMenu jm
, String name
, int key
) {
204 addMenuItemCallback(jm
, name
, "jcallback_menu_key_event", key
);
207 private JMenuItem
addMenuItemCallback(JMenu jm
, String name
, final String callback
, final int arg
) {
208 return addMenuItemCallback(jm
, name
, callback
, new int[] {arg
});
211 private JMenuItem
addMenuItemCallback(JMenu jm
, String name
, final String callback
) {
212 return addMenuItemCallback(jm
, name
, callback
, new int[0]);
215 private JMenuItem
addMenuItemCallback(JMenu jm
, String name
, final String callback
, final int[] args
) {
218 typeMenu
.add(jmi
= new JCheckBoxMenuItem(name
));
220 jm
.add(jmi
= new JMenuItem(name
));
221 jmi
.addActionListener(new ActionListener() {
222 public void actionPerformed(ActionEvent e
) {
223 runtimeCall(callback
, args
);
229 protected void runtimeCall(String func
, int[] args
) {
230 if (runtimeCallWithResult(func
, args
) == 42 && mainWindow
!= null
) {
235 protected int runtimeCallWithResult(String func
, int[] args
) {
237 return runtime
.call(func
, args
);
238 } catch (Runtime
.CallException ex
) {
239 ex
.printStackTrace();
244 private void buildConfigureMenuItem() {
245 if (typeMenu
.isVisible()) {
246 typeMenu
.addSeparator();
248 typeMenu
.setVisible(true
);
250 addMenuItemCallback(typeMenu
, "Custom...", "jcallback_config_event", CFG_SETTINGS
);
253 private void addTypeItem(String name
, final int ptrGameParams
) {
254 typeMenu
.setVisible(true
);
255 addMenuItemCallback(typeMenu
, name
, "jcallback_preset_event", ptrGameParams
);
258 public int call(int cmd
, int arg1
, int arg2
, int arg3
) {
261 case 0: // initialize
262 if (mainWindow
!= null
) mainWindow
.setTitle(runtime
.cstring(arg1
));
263 if ((arg2
& 1) != 0) buildConfigureMenuItem();
264 if ((arg2
& 2) != 0) addStatusBar();
265 if ((arg2
& 4) != 0) solveCommand
.setEnabled(true
);
266 colors
= new Color
[arg3
];
268 case 1: // Type menu item
269 addTypeItem(runtime
.cstring(arg1
), arg2
);
271 case 2: // MessageBox
272 JOptionPane
.showMessageDialog(this, runtime
.cstring(arg2
), runtime
.cstring(arg1
), arg3
== 0 ? JOptionPane
.INFORMATION_MESSAGE
: JOptionPane
.ERROR_MESSAGE
);
275 pp
.setPreferredSize(new Dimension(arg1
, arg2
));
276 if (mainWindow
!= null
) mainWindow
.pack();
278 if (mainWindow
!= null
) mainWindow
.setVisible(true
);
280 case 4: // drawing tasks
283 String text
= runtime
.cstring(arg2
);
284 if (text
.equals("")) text
= " ";
285 System
.out
.println("status '" + text
+ "'");
286 statusBar
.setText(text
); break;
288 gg
= pp
.backBuffer
.createGraphics();
289 if (arg2
!= 0 || arg3
!= 0) {
290 gg
.setColor(Color
.black
);
291 gg
.fillRect(0, 0, arg2
, getHeight());
292 gg
.fillRect(0, 0, getWidth(), arg3
);
293 gg
.fillRect(getWidth() - arg2
, 0, arg2
, getHeight());
294 gg
.fillRect(0, getHeight() - arg3
, getWidth(), arg3
);
295 gg
.setClip(arg2
, arg3
, getWidth()-2*arg2
, getHeight()-2*arg3
);
298 case 2: gg
.dispose(); pp
.repaint(); break;
299 case 3: gg
.setClip(arg2
, arg3
, xarg1
, xarg2
); break;
301 if (arg2
== 0 && arg3
== 0) {
302 gg
.fillRect(0, 0, getWidth(), getHeight());
304 gg
.setClip(arg2
, arg3
, getWidth()-2*arg2
, getHeight()-2*arg3
);
308 gg
.setColor(colors
[xarg3
]);
309 gg
.fillRect(arg2
, arg3
, xarg1
, xarg2
);
312 gg
.setColor(colors
[xarg3
]);
313 gg
.drawLine(arg2
, arg3
, xarg1
, xarg2
);
316 xPoints
= new int[arg2
];
317 yPoints
= new int[arg2
];
321 gg
.setColor(colors
[arg3
]);
322 gg
.fillPolygon(xPoints
, yPoints
, xPoints
.length
);
324 gg
.setColor(colors
[arg2
]);
325 gg
.drawPolygon(xPoints
, yPoints
, xPoints
.length
);
329 gg
.setColor(colors
[arg3
]);
330 gg
.fillOval(xarg1
-xarg3
, xarg2
-xarg3
, xarg3
*2, xarg3
*2);
332 gg
.setColor(colors
[arg2
]);
333 gg
.drawOval(xarg1
-xarg3
, xarg2
-xarg3
, xarg3
*2, xarg3
*2);
336 for(int i
=0; i
<blitters
.length
; i
++) {
337 if (blitters
[i
] == null
) {
338 blitters
[i
] = new BufferedImage(arg2
, arg3
, BufferedImage
.TYPE_3BYTE_BGR
);
342 throw new RuntimeException("No free blitter found!");
343 case 11: blitters
[arg2
] = null
; break;
345 timer
.start(); break;
350 case 5: // more arguments
355 case 6: // polygon vertex
360 gg
.setColor(colors
[arg2
]);
362 String text
= runtime
.cstring(arg3
);
363 Font ft
= new Font((xarg3
& 0x10) != 0 ?
"Monospaced" : "Dialog",
365 int height100
= this.getFontMetrics(ft
).getHeight();
366 ft
= ft
.deriveFont(arg1
* 100 / (float)height100
);
367 FontMetrics fm
= this.getFontMetrics(ft
);
368 int asc
= fm
.getAscent(), desc
= fm
.getDescent();
369 if ((xarg3
& ALIGN_VCENTRE
) != 0)
370 xarg2
+= asc
- (asc
+desc
)/2;
373 int wid
= fm
.stringWidth(text
);
374 if ((xarg3
& ALIGN_HCENTRE
) != 0)
376 else if ((xarg3
& ALIGN_HRIGHT
) != 0)
379 gg
.drawString(text
, xarg1
, xarg2
);
382 case 8: // blitter_save
383 Graphics g2
= blitters
[arg1
].createGraphics();
384 g2
.drawImage(pp
.backBuffer
, 0, 0, blitters
[arg1
].getWidth(), blitters
[arg1
].getHeight(),
385 arg2
, arg3
, arg2
+ blitters
[arg1
].getWidth(), arg3
+ blitters
[arg1
].getHeight(), this);
388 case 9: // blitter_load
389 gg
.drawImage(blitters
[arg1
], arg2
, arg3
, this);
391 case 10: // dialog_init
392 dlg
= new ConfigDialog(this, runtime
.cstring(arg1
));
394 case 11: // dialog_add_control
400 String name
= runtime
.cstring(xarg3
);
403 dlg
.addTextBox(ptr
, name
, runtime
.cstring(sval_ptr
));
406 dlg
.addCheckBox(ptr
, name
, ival
!= 0);
409 dlg
.addComboBox(ptr
, name
, runtime
.cstring(sval_ptr
), ival
);
417 case 13: // tick a menu item
418 if (arg1
< 0) arg1
= typeMenu
.getItemCount() - 1;
419 for (int i
= 0; i
< typeMenu
.getItemCount(); i
++) {
420 if (typeMenu
.getMenuComponent(i
) instanceof JCheckBoxMenuItem
) {
421 ((JCheckBoxMenuItem
)typeMenu
.getMenuComponent(i
)).setSelected(arg1
== i
);
426 if (cmd
>= 1024 && cmd
< 2048) { // palette
427 colors
[cmd
-1024] = new Color(arg1
, arg2
, arg3
);
430 pp
.setBackground(colors
[0]);
431 if (statusBar
!= null
) statusBar
.setBackground(colors
[0]);
432 this.setBackground(colors
[0]);
436 } catch (Throwable ex
) {
437 ex
.printStackTrace();
443 private void addStatusBar() {
444 statusBar
= new JLabel("test");
445 statusBar
.setBorder(new BevelBorder(BevelBorder
.LOWERED
));
446 getContentPane().add(BorderLayout
.SOUTH
,statusBar
);
450 public static void main(String
[] args
) {
451 final PuzzleApplet a
= new PuzzleApplet();
452 JFrame jf
= new JFrame("Loading...");
453 jf
.getContentPane().setLayout(new BorderLayout());
454 jf
.getContentPane().add(a
, BorderLayout
.CENTER
);
458 jf
.setDefaultCloseOperation(JFrame
.EXIT_ON_CLOSE
);
459 jf
.addWindowListener(new WindowAdapter() {
460 public void windowClosing(WindowEvent e
) {
468 public static class PuzzlePanel
extends JPanel
{
470 private static final long serialVersionUID
= 1L;
471 protected BufferedImage backBuffer
;
473 public PuzzlePanel() {
474 setPreferredSize(new Dimension(100,100));
475 createBackBuffer(100,100, Color
.black
);
478 public void createBackBuffer(int w
, int h
, Color bg
) {
479 backBuffer
= new BufferedImage(w
,h
, BufferedImage
.TYPE_3BYTE_BGR
);
480 Graphics g
= backBuffer
.createGraphics();
482 g
.fillRect(0, 0, w
, h
);
486 protected void paintComponent(Graphics g
) {
487 g
.drawImage(backBuffer
, 0, 0, this);
491 public static class ConfigComponent
{
493 public int configItemPointer
;
494 public JComponent component
;
496 public ConfigComponent(int type
, int configItemPointer
, JComponent component
) {
498 this.configItemPointer
= configItemPointer
;
499 this.component
= component
;
503 public class ConfigDialog
extends JDialog
{
505 private GridBagConstraints gbcLeft
= new GridBagConstraints(
506 GridBagConstraints
.RELATIVE
, GridBagConstraints
.RELATIVE
, 1, 1,
507 0, 0, GridBagConstraints
.WEST
, GridBagConstraints
.NONE
,
508 new Insets(0, 0, 0, 0), 0, 0);
509 private GridBagConstraints gbcRight
= new GridBagConstraints(
510 GridBagConstraints
.RELATIVE
, GridBagConstraints
.RELATIVE
,
511 GridBagConstraints
.REMAINDER
, 1, 1.0, 0,
512 GridBagConstraints
.CENTER
, GridBagConstraints
.HORIZONTAL
,
513 new Insets(5, 5, 5, 5), 0, 0);
514 private GridBagConstraints gbcBottom
= new GridBagConstraints(
515 GridBagConstraints
.RELATIVE
, GridBagConstraints
.RELATIVE
,
516 GridBagConstraints
.REMAINDER
, GridBagConstraints
.REMAINDER
,
517 1.0, 1.0, GridBagConstraints
.CENTER
,
518 GridBagConstraints
.HORIZONTAL
, new Insets(5, 5, 5, 5), 0, 0);
520 private static final long serialVersionUID
= 1L;
521 private List components
= new ArrayList();
523 public ConfigDialog(JApplet parent
, String title
) {
524 super(JOptionPane
.getFrameForComponent(parent
), title
, true
);
525 getContentPane().setLayout(new GridBagLayout());
528 public void addTextBox(int ptr
, String name
, String value
) {
529 getContentPane().add(new JLabel(name
), gbcLeft
);
530 JComponent c
= new JTextField(value
, 25);
531 getContentPane().add(c
, gbcRight
);
532 components
.add(new ConfigComponent(C_STRING
, ptr
, c
));
536 public void addCheckBox(int ptr
, String name
, boolean selected
) {
537 JComponent c
= new JCheckBox(name
, selected
);
538 getContentPane().add(c
, gbcRight
);
539 components
.add(new ConfigComponent(C_BOOLEAN
, ptr
, c
));
542 public void addComboBox(int ptr
, String name
, String values
, int selected
) {
543 getContentPane().add(new JLabel(name
), gbcLeft
);
544 StringTokenizer st
= new StringTokenizer(values
.substring(1), values
.substring(0,1));
545 JComboBox c
= new JComboBox();
546 c
.setEditable(false
);
547 while(st
.hasMoreTokens())
548 c
.addItem(st
.nextToken());
549 c
.setSelectedIndex(selected
);
550 getContentPane().add(c
, gbcRight
);
551 components
.add(new ConfigComponent(C_CHOICES
, ptr
, c
));
554 public void finish() {
555 JPanel buttons
= new JPanel(new GridLayout(1, 2, 5, 5));
556 getContentPane().add(buttons
, gbcBottom
);
558 buttons
.add(b
=new JButton("OK"));
559 b
.addActionListener(new ActionListener() {
560 public void actionPerformed(ActionEvent e
) {
565 getRootPane().setDefaultButton(b
);
566 buttons
.add(b
=new JButton("Cancel"));
567 b
.addActionListener(new ActionListener() {
568 public void actionPerformed(ActionEvent e
) {
572 setDefaultCloseOperation(DISPOSE_ON_CLOSE
);
574 setLocationRelativeTo(null
);
577 private void save() {
578 for (int i
= 0; i
< components
.size(); i
++) {
579 ConfigComponent cc
= (ConfigComponent
) components
.get(i
);
582 JTextField jtf
= (JTextField
)cc
.component
;
583 runtimeCall("jcallback_config_set_string", new int[] {cc
.configItemPointer
, runtime
.strdup(jtf
.getText())});
586 JCheckBox jcb
= (JCheckBox
)cc
.component
;
587 runtimeCall("jcallback_config_set_boolean", new int[] {cc
.configItemPointer
, jcb
.isSelected()?
1:0});
590 JComboBox jcm
= (JComboBox
)cc
.component
;
591 runtimeCall("jcallback_config_set_boolean", new int[] {cc
.configItemPointer
, jcm
.getSelectedIndex()});
595 runtimeCall("jcallback_config_ok", new int[0]);