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 System
.out
.println("status '" + text
+ "'");
300 statusBar
.setText(text
); break;
302 gg
= pp
.backBuffer
.createGraphics();
303 if (arg2
!= 0 || arg3
!= 0) {
304 gg
.setColor(Color
.black
);
305 gg
.fillRect(0, 0, arg2
, getHeight());
306 gg
.fillRect(0, 0, getWidth(), arg3
);
307 gg
.fillRect(getWidth() - arg2
, 0, arg2
, getHeight());
308 gg
.fillRect(0, getHeight() - arg3
, getWidth(), arg3
);
309 gg
.setClip(arg2
, arg3
, getWidth()-2*arg2
, getHeight()-2*arg3
);
312 case 2: gg
.dispose(); pp
.repaint(); break;
313 case 3: gg
.setClip(arg2
, arg3
, xarg1
, xarg2
); break;
315 if (arg2
== 0 && arg3
== 0) {
316 gg
.fillRect(0, 0, getWidth(), getHeight());
318 gg
.setClip(arg2
, arg3
, getWidth()-2*arg2
, getHeight()-2*arg3
);
322 gg
.setColor(colors
[xarg3
]);
323 gg
.fillRect(arg2
, arg3
, xarg1
, xarg2
);
326 gg
.setColor(colors
[xarg3
]);
327 gg
.drawLine(arg2
, arg3
, xarg1
, xarg2
);
330 xPoints
= new int[arg2
];
331 yPoints
= new int[arg2
];
335 gg
.setColor(colors
[arg3
]);
336 gg
.fillPolygon(xPoints
, yPoints
, xPoints
.length
);
338 gg
.setColor(colors
[arg2
]);
339 gg
.drawPolygon(xPoints
, yPoints
, xPoints
.length
);
343 gg
.setColor(colors
[arg3
]);
344 gg
.fillOval(xarg1
-xarg3
, xarg2
-xarg3
, xarg3
*2, xarg3
*2);
346 gg
.setColor(colors
[arg2
]);
347 gg
.drawOval(xarg1
-xarg3
, xarg2
-xarg3
, xarg3
*2, xarg3
*2);
350 for(int i
=0; i
<blitters
.length
; i
++) {
351 if (blitters
[i
] == null) {
352 blitters
[i
] = new BufferedImage(arg2
, arg3
, BufferedImage
.TYPE_3BYTE_BGR
);
356 throw new RuntimeException("No free blitter found!");
357 case 11: blitters
[arg2
] = null; break;
359 timer
.start(); break;
364 case 5: // more arguments
369 case 6: // polygon vertex
374 gg
.setColor(colors
[arg2
]);
376 String text
= runtime
.cstring(arg3
);
377 Font ft
= new Font((xarg3
& 0x10) != 0 ?
"Monospaced" : "Dialog",
379 int height100
= this.getFontMetrics(ft
).getHeight();
380 ft
= ft
.deriveFont(arg1
* 100 / (float)height100
);
381 FontMetrics fm
= this.getFontMetrics(ft
);
382 int asc
= fm
.getAscent(), desc
= fm
.getDescent();
383 if ((xarg3
& ALIGN_VCENTRE
) != 0)
384 xarg2
+= asc
- (asc
+desc
)/2;
385 int wid
= fm
.stringWidth(text
);
386 if ((xarg3
& ALIGN_HCENTRE
) != 0)
388 else if ((xarg3
& ALIGN_HRIGHT
) != 0)
391 gg
.drawString(text
, xarg1
, xarg2
);
394 case 8: // blitter_save
395 Graphics g2
= blitters
[arg1
].createGraphics();
396 g2
.drawImage(pp
.backBuffer
, 0, 0, blitters
[arg1
].getWidth(), blitters
[arg1
].getHeight(),
397 arg2
, arg3
, arg2
+ blitters
[arg1
].getWidth(), arg3
+ blitters
[arg1
].getHeight(), this);
400 case 9: // blitter_load
401 gg
.drawImage(blitters
[arg1
], arg2
, arg3
, this);
403 case 10: // dialog_init
404 dlg
= new ConfigDialog(this, runtime
.cstring(arg1
));
406 case 11: // dialog_add_control
412 String name
= runtime
.cstring(xarg3
);
415 dlg
.addTextBox(ptr
, name
, runtime
.cstring(sval_ptr
));
418 dlg
.addCheckBox(ptr
, name
, ival
!= 0);
421 dlg
.addComboBox(ptr
, name
, runtime
.cstring(sval_ptr
), ival
);
429 case 13: // tick a menu item
430 if (arg1
< 0) arg1
= typeMenu
.getItemCount() - 1;
431 for (int i
= 0; i
< typeMenu
.getItemCount(); i
++) {
432 if (typeMenu
.getMenuComponent(i
) instanceof JCheckBoxMenuItem
) {
433 ((JCheckBoxMenuItem
)typeMenu
.getMenuComponent(i
)).setSelected(arg1
== i
);
438 if (cmd
>= 1024 && cmd
< 2048) { // palette
439 colors
[cmd
-1024] = new Color(arg1
, arg2
, arg3
);
442 pp
.setBackground(colors
[0]);
443 if (statusBar
!= null) statusBar
.setBackground(colors
[0]);
444 this.setBackground(colors
[0]);
448 } catch (Throwable ex
) {
449 ex
.printStackTrace();
455 private void addStatusBar() {
456 statusBar
= new JLabel("test");
457 statusBar
.setBorder(new BevelBorder(BevelBorder
.LOWERED
));
458 getContentPane().add(BorderLayout
.SOUTH
,statusBar
);
462 public static void main(String
[] args
) {
463 final PuzzleApplet a
= new PuzzleApplet();
464 JFrame jf
= new JFrame("Loading...");
465 jf
.getContentPane().setLayout(new BorderLayout());
466 jf
.getContentPane().add(a
, BorderLayout
.CENTER
);
470 jf
.setDefaultCloseOperation(JFrame
.EXIT_ON_CLOSE
);
471 jf
.addWindowListener(new WindowAdapter() {
472 public void windowClosing(WindowEvent e
) {
480 public static class PuzzlePanel
extends JPanel
{
482 private static final long serialVersionUID
= 1L;
483 protected BufferedImage backBuffer
;
485 public PuzzlePanel() {
486 setPreferredSize(new Dimension(100,100));
487 createBackBuffer(100,100, Color
.black
);
490 public void createBackBuffer(int w
, int h
, Color bg
) {
491 if (w
> 0 && h
> 0) {
492 backBuffer
= new BufferedImage(w
,h
, BufferedImage
.TYPE_3BYTE_BGR
);
493 Graphics g
= backBuffer
.createGraphics();
495 g
.fillRect(0, 0, w
, h
);
500 protected void paintComponent(Graphics g
) {
501 g
.drawImage(backBuffer
, 0, 0, this);
505 public static class ConfigComponent
{
507 public int configItemPointer
;
508 public JComponent component
;
510 public ConfigComponent(int type
, int configItemPointer
, JComponent component
) {
512 this.configItemPointer
= configItemPointer
;
513 this.component
= component
;
517 public class ConfigDialog
extends JDialog
{
519 private GridBagConstraints gbcLeft
= new GridBagConstraints(
520 GridBagConstraints
.RELATIVE
, GridBagConstraints
.RELATIVE
, 1, 1,
521 0, 0, GridBagConstraints
.WEST
, GridBagConstraints
.NONE
,
522 new Insets(0, 0, 0, 0), 0, 0);
523 private GridBagConstraints gbcRight
= new GridBagConstraints(
524 GridBagConstraints
.RELATIVE
, GridBagConstraints
.RELATIVE
,
525 GridBagConstraints
.REMAINDER
, 1, 1.0, 0,
526 GridBagConstraints
.CENTER
, GridBagConstraints
.HORIZONTAL
,
527 new Insets(5, 5, 5, 5), 0, 0);
528 private GridBagConstraints gbcBottom
= new GridBagConstraints(
529 GridBagConstraints
.RELATIVE
, GridBagConstraints
.RELATIVE
,
530 GridBagConstraints
.REMAINDER
, GridBagConstraints
.REMAINDER
,
531 1.0, 1.0, GridBagConstraints
.CENTER
,
532 GridBagConstraints
.HORIZONTAL
, new Insets(5, 5, 5, 5), 0, 0);
534 private static final long serialVersionUID
= 1L;
535 private List components
= new ArrayList();
537 public ConfigDialog(JApplet parent
, String title
) {
538 super(JOptionPane
.getFrameForComponent(parent
), title
, true);
539 getContentPane().setLayout(new GridBagLayout());
542 public void addTextBox(int ptr
, String name
, String value
) {
543 getContentPane().add(new JLabel(name
), gbcLeft
);
544 JComponent c
= new JTextField(value
, 25);
545 getContentPane().add(c
, gbcRight
);
546 components
.add(new ConfigComponent(C_STRING
, ptr
, c
));
550 public void addCheckBox(int ptr
, String name
, boolean selected
) {
551 JComponent c
= new JCheckBox(name
, selected
);
552 getContentPane().add(c
, gbcRight
);
553 components
.add(new ConfigComponent(C_BOOLEAN
, ptr
, c
));
556 public void addComboBox(int ptr
, String name
, String values
, int selected
) {
557 getContentPane().add(new JLabel(name
), gbcLeft
);
558 StringTokenizer st
= new StringTokenizer(values
.substring(1), values
.substring(0,1));
559 JComboBox c
= new JComboBox();
560 c
.setEditable(false);
561 while(st
.hasMoreTokens())
562 c
.addItem(st
.nextToken());
563 c
.setSelectedIndex(selected
);
564 getContentPane().add(c
, gbcRight
);
565 components
.add(new ConfigComponent(C_CHOICES
, ptr
, c
));
568 public void finish() {
569 JPanel buttons
= new JPanel(new GridLayout(1, 2, 5, 5));
570 getContentPane().add(buttons
, gbcBottom
);
572 buttons
.add(b
=new JButton("OK"));
573 b
.addActionListener(new ActionListener() {
574 public void actionPerformed(ActionEvent e
) {
579 getRootPane().setDefaultButton(b
);
580 buttons
.add(b
=new JButton("Cancel"));
581 b
.addActionListener(new ActionListener() {
582 public void actionPerformed(ActionEvent e
) {
586 setDefaultCloseOperation(DISPOSE_ON_CLOSE
);
588 setLocationRelativeTo(null);
591 private void save() {
592 for (int i
= 0; i
< components
.size(); i
++) {
593 ConfigComponent cc
= (ConfigComponent
) components
.get(i
);
596 JTextField jtf
= (JTextField
)cc
.component
;
597 runtimeCall("jcallback_config_set_string", new int[] {cc
.configItemPointer
, runtime
.strdup(jtf
.getText())});
600 JCheckBox jcb
= (JCheckBox
)cc
.component
;
601 runtimeCall("jcallback_config_set_boolean", new int[] {cc
.configItemPointer
, jcb
.isSelected()?
1:0});
604 JComboBox jcm
= (JComboBox
)cc
.component
;
605 runtimeCall("jcallback_config_set_choice", new int[] {cc
.configItemPointer
, jcm
.getSelectedIndex()});
609 runtimeCall("jcallback_config_ok", new int[0]);