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
);
303 gg
= pp
.backBuffer
.createGraphics();
304 if (arg2
!= 0 || arg3
!= 0 ||
305 arg2
+ xarg2
!= getWidth() ||
306 arg3
+ xarg3
!= getHeight()) {
307 int left
= arg2
, right
= arg2
+ xarg2
;
308 int top
= arg3
, bottom
= arg3
+ xarg3
;
309 int width
= getWidth(), height
= getHeight();
310 gg
.setColor(colors
!= null ? colors
[0] : Color
.black
);
311 gg
.fillRect(0, 0, left
, height
);
312 gg
.fillRect(right
, 0, width
-right
, height
);
313 gg
.fillRect(0, 0, width
, top
);
314 gg
.fillRect(0, bottom
, width
, height
-bottom
);
315 gg
.setClip(left
, top
, right
-left
, bottom
-top
);
318 case 2: gg
.dispose(); pp
.repaint(); break;
319 case 3: gg
.setClip(arg2
, arg3
, xarg1
, xarg2
); break;
321 if (arg2
== 0 && arg3
== 0) {
322 gg
.fillRect(0, 0, getWidth(), getHeight());
324 gg
.setClip(arg2
, arg3
, getWidth()-2*arg2
, getHeight()-2*arg3
);
328 gg
.setColor(colors
[xarg3
]);
329 gg
.fillRect(arg2
, arg3
, xarg1
, xarg2
);
332 gg
.setColor(colors
[xarg3
]);
333 gg
.drawLine(arg2
, arg3
, xarg1
, xarg2
);
336 xPoints
= new int[arg2
];
337 yPoints
= new int[arg2
];
341 gg
.setColor(colors
[arg3
]);
342 gg
.fillPolygon(xPoints
, yPoints
, xPoints
.length
);
344 gg
.setColor(colors
[arg2
]);
345 gg
.drawPolygon(xPoints
, yPoints
, xPoints
.length
);
349 gg
.setColor(colors
[arg3
]);
350 gg
.fillOval(xarg1
-xarg3
, xarg2
-xarg3
, xarg3
*2, xarg3
*2);
352 gg
.setColor(colors
[arg2
]);
353 gg
.drawOval(xarg1
-xarg3
, xarg2
-xarg3
, xarg3
*2, xarg3
*2);
356 for(int i
=0; i
<blitters
.length
; i
++) {
357 if (blitters
[i
] == null) {
358 blitters
[i
] = new BufferedImage(arg2
, arg3
, BufferedImage
.TYPE_3BYTE_BGR
);
362 throw new RuntimeException("No free blitter found!");
363 case 11: blitters
[arg2
] = null; break;
365 timer
.start(); break;
370 case 5: // more arguments
375 case 6: // polygon vertex
380 gg
.setColor(colors
[arg2
]);
382 String text
= runtime
.utfstring(arg3
);
383 Font ft
= new Font((xarg3
& 0x10) != 0 ?
"Monospaced" : "Dialog",
385 int height100
= this.getFontMetrics(ft
).getHeight();
386 ft
= ft
.deriveFont(arg1
* 100 / (float)height100
);
387 FontMetrics fm
= this.getFontMetrics(ft
);
388 int asc
= fm
.getAscent(), desc
= fm
.getDescent();
389 if ((xarg3
& ALIGN_VCENTRE
) != 0)
390 xarg2
+= asc
- (asc
+desc
)/2;
391 int wid
= fm
.stringWidth(text
);
392 if ((xarg3
& ALIGN_HCENTRE
) != 0)
394 else if ((xarg3
& ALIGN_HRIGHT
) != 0)
397 gg
.drawString(text
, xarg1
, xarg2
);
400 case 8: // blitter_save
401 Graphics g2
= blitters
[arg1
].createGraphics();
402 g2
.drawImage(pp
.backBuffer
, 0, 0, blitters
[arg1
].getWidth(), blitters
[arg1
].getHeight(),
403 arg2
, arg3
, arg2
+ blitters
[arg1
].getWidth(), arg3
+ blitters
[arg1
].getHeight(), this);
406 case 9: // blitter_load
407 gg
.drawImage(blitters
[arg1
], arg2
, arg3
, this);
409 case 10: // dialog_init
410 dlg
= new ConfigDialog(this, runtime
.cstring(arg1
));
412 case 11: // dialog_add_control
418 String name
= runtime
.cstring(xarg3
);
421 dlg
.addTextBox(ptr
, name
, runtime
.cstring(sval_ptr
));
424 dlg
.addCheckBox(ptr
, name
, ival
!= 0);
427 dlg
.addComboBox(ptr
, name
, runtime
.cstring(sval_ptr
), ival
);
435 case 13: // tick a menu item
436 if (arg1
< 0) arg1
= typeMenu
.getItemCount() - 1;
437 for (int i
= 0; i
< typeMenu
.getItemCount(); i
++) {
438 if (typeMenu
.getMenuComponent(i
) instanceof JCheckBoxMenuItem
) {
439 ((JCheckBoxMenuItem
)typeMenu
.getMenuComponent(i
)).setSelected(arg1
== i
);
444 if (cmd
>= 1024 && cmd
< 2048) { // palette
445 colors
[cmd
-1024] = new Color(arg1
, arg2
, arg3
);
448 pp
.setBackground(colors
[0]);
449 if (statusBar
!= null) statusBar
.setBackground(colors
[0]);
450 this.setBackground(colors
[0]);
454 } catch (Throwable ex
) {
455 ex
.printStackTrace();
461 private void addStatusBar() {
462 statusBar
= new JLabel("test");
463 statusBar
.setBorder(new BevelBorder(BevelBorder
.LOWERED
));
464 getContentPane().add(BorderLayout
.SOUTH
,statusBar
);
468 public static void main(String
[] args
) {
469 final PuzzleApplet a
= new PuzzleApplet();
470 JFrame jf
= new JFrame("Loading...");
471 jf
.getContentPane().setLayout(new BorderLayout());
472 jf
.getContentPane().add(a
, BorderLayout
.CENTER
);
476 jf
.setDefaultCloseOperation(JFrame
.EXIT_ON_CLOSE
);
477 jf
.addWindowListener(new WindowAdapter() {
478 public void windowClosing(WindowEvent e
) {
486 public static class PuzzlePanel
extends JPanel
{
488 private static final long serialVersionUID
= 1L;
489 protected BufferedImage backBuffer
;
491 public PuzzlePanel() {
492 setPreferredSize(new Dimension(100,100));
493 createBackBuffer(100,100, Color
.black
);
496 public void createBackBuffer(int w
, int h
, Color bg
) {
497 if (w
> 0 && h
> 0) {
498 backBuffer
= new BufferedImage(w
,h
, BufferedImage
.TYPE_3BYTE_BGR
);
499 Graphics g
= backBuffer
.createGraphics();
501 g
.fillRect(0, 0, w
, h
);
506 protected void paintComponent(Graphics g
) {
507 g
.drawImage(backBuffer
, 0, 0, this);
511 public static class ConfigComponent
{
513 public int configItemPointer
;
514 public JComponent component
;
516 public ConfigComponent(int type
, int configItemPointer
, JComponent component
) {
518 this.configItemPointer
= configItemPointer
;
519 this.component
= component
;
523 public class ConfigDialog
extends JDialog
{
525 private GridBagConstraints gbcLeft
= new GridBagConstraints(
526 GridBagConstraints
.RELATIVE
, GridBagConstraints
.RELATIVE
, 1, 1,
527 0, 0, GridBagConstraints
.WEST
, GridBagConstraints
.NONE
,
528 new Insets(0, 0, 0, 0), 0, 0);
529 private GridBagConstraints gbcRight
= new GridBagConstraints(
530 GridBagConstraints
.RELATIVE
, GridBagConstraints
.RELATIVE
,
531 GridBagConstraints
.REMAINDER
, 1, 1.0, 0,
532 GridBagConstraints
.CENTER
, GridBagConstraints
.HORIZONTAL
,
533 new Insets(5, 5, 5, 5), 0, 0);
534 private GridBagConstraints gbcBottom
= new GridBagConstraints(
535 GridBagConstraints
.RELATIVE
, GridBagConstraints
.RELATIVE
,
536 GridBagConstraints
.REMAINDER
, GridBagConstraints
.REMAINDER
,
537 1.0, 1.0, GridBagConstraints
.CENTER
,
538 GridBagConstraints
.HORIZONTAL
, new Insets(5, 5, 5, 5), 0, 0);
540 private static final long serialVersionUID
= 1L;
541 private List components
= new ArrayList();
543 public ConfigDialog(JApplet parent
, String title
) {
544 super(JOptionPane
.getFrameForComponent(parent
), title
, true);
545 getContentPane().setLayout(new GridBagLayout());
548 public void addTextBox(int ptr
, String name
, String value
) {
549 getContentPane().add(new JLabel(name
), gbcLeft
);
550 JComponent c
= new JTextField(value
, 25);
551 getContentPane().add(c
, gbcRight
);
552 components
.add(new ConfigComponent(C_STRING
, ptr
, c
));
556 public void addCheckBox(int ptr
, String name
, boolean selected
) {
557 JComponent c
= new JCheckBox(name
, selected
);
558 getContentPane().add(c
, gbcRight
);
559 components
.add(new ConfigComponent(C_BOOLEAN
, ptr
, c
));
562 public void addComboBox(int ptr
, String name
, String values
, int selected
) {
563 getContentPane().add(new JLabel(name
), gbcLeft
);
564 StringTokenizer st
= new StringTokenizer(values
.substring(1), values
.substring(0,1));
565 JComboBox c
= new JComboBox();
566 c
.setEditable(false);
567 while(st
.hasMoreTokens())
568 c
.addItem(st
.nextToken());
569 c
.setSelectedIndex(selected
);
570 getContentPane().add(c
, gbcRight
);
571 components
.add(new ConfigComponent(C_CHOICES
, ptr
, c
));
574 public void finish() {
575 JPanel buttons
= new JPanel(new GridLayout(1, 2, 5, 5));
576 getContentPane().add(buttons
, gbcBottom
);
578 buttons
.add(b
=new JButton("OK"));
579 b
.addActionListener(new ActionListener() {
580 public void actionPerformed(ActionEvent e
) {
585 getRootPane().setDefaultButton(b
);
586 buttons
.add(b
=new JButton("Cancel"));
587 b
.addActionListener(new ActionListener() {
588 public void actionPerformed(ActionEvent e
) {
592 setDefaultCloseOperation(DISPOSE_ON_CLOSE
);
594 setLocationRelativeTo(null);
597 private void save() {
598 for (int i
= 0; i
< components
.size(); i
++) {
599 ConfigComponent cc
= (ConfigComponent
) components
.get(i
);
602 JTextField jtf
= (JTextField
)cc
.component
;
603 runtimeCall("jcallback_config_set_string", new int[] {cc
.configItemPointer
, runtime
.strdup(jtf
.getText())});
606 JCheckBox jcb
= (JCheckBox
)cc
.component
;
607 runtimeCall("jcallback_config_set_boolean", new int[] {cc
.configItemPointer
, jcb
.isSelected()?
1:0});
610 JComboBox jcm
= (JComboBox
)cc
.component
;
611 runtimeCall("jcallback_config_set_choice", new int[] {cc
.configItemPointer
, jcm
.getSelectedIndex()});
615 runtimeCall("jcallback_config_ok", new int[0]);