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 System
.out
.println("ooh " + gameid
);
183 if (gameid
== null
) {
186 puzzle_args
= new String
[2];
187 puzzle_args
[0] = "puzzle";
188 puzzle_args
[1] = gameid
;
190 SwingUtilities
.invokeLater(new Runnable() {
192 runtime
.start(puzzle_args
);
196 } catch (Exception ex
) {
197 ex
.printStackTrace();
201 public void destroy() {
202 SwingUtilities
.invokeLater(new Runnable() {
205 if (mainWindow
!= null
) {
206 mainWindow
.dispose();
213 protected void handleResized() {
214 pp
.createBackBuffer(pp
.getWidth(), pp
.getHeight(), colors
[0]);
215 runtimeCall("jcallback_resize", new int[] {pp
.getWidth(), pp
.getHeight()});
218 private void addMenuItemWithKey(JMenu jm
, String name
, int key
) {
219 addMenuItemCallback(jm
, name
, "jcallback_menu_key_event", key
);
222 private JMenuItem
addMenuItemCallback(JMenu jm
, String name
, final String callback
, final int arg
) {
223 return addMenuItemCallback(jm
, name
, callback
, new int[] {arg
});
226 private JMenuItem
addMenuItemCallback(JMenu jm
, String name
, final String callback
) {
227 return addMenuItemCallback(jm
, name
, callback
, new int[0]);
230 private JMenuItem
addMenuItemCallback(JMenu jm
, String name
, final String callback
, final int[] args
) {
233 typeMenu
.add(jmi
= new JCheckBoxMenuItem(name
));
235 jm
.add(jmi
= new JMenuItem(name
));
236 jmi
.addActionListener(new ActionListener() {
237 public void actionPerformed(ActionEvent e
) {
238 runtimeCall(callback
, args
);
244 protected void runtimeCall(String func
, int[] args
) {
245 if (runtimeCallWithResult(func
, args
) == 42 && mainWindow
!= null
) {
250 protected int runtimeCallWithResult(String func
, int[] args
) {
252 return runtime
.call(func
, args
);
253 } catch (Runtime
.CallException ex
) {
254 ex
.printStackTrace();
259 private void buildConfigureMenuItem() {
260 if (typeMenu
.isVisible()) {
261 typeMenu
.addSeparator();
263 typeMenu
.setVisible(true
);
265 addMenuItemCallback(typeMenu
, "Custom...", "jcallback_config_event", CFG_SETTINGS
);
268 private void addTypeItem(String name
, final int ptrGameParams
) {
269 typeMenu
.setVisible(true
);
270 addMenuItemCallback(typeMenu
, name
, "jcallback_preset_event", ptrGameParams
);
273 public int call(int cmd
, int arg1
, int arg2
, int arg3
) {
276 case 0: // initialize
277 if (mainWindow
!= null
) mainWindow
.setTitle(runtime
.cstring(arg1
));
278 if ((arg2
& 1) != 0) buildConfigureMenuItem();
279 if ((arg2
& 2) != 0) addStatusBar();
280 if ((arg2
& 4) != 0) solveCommand
.setEnabled(true
);
281 colors
= new Color
[arg3
];
283 case 1: // Type menu item
284 addTypeItem(runtime
.cstring(arg1
), arg2
);
286 case 2: // MessageBox
287 JOptionPane
.showMessageDialog(this, runtime
.cstring(arg2
), runtime
.cstring(arg1
), arg3
== 0 ? JOptionPane
.INFORMATION_MESSAGE
: JOptionPane
.ERROR_MESSAGE
);
290 pp
.setPreferredSize(new Dimension(arg1
, arg2
));
291 if (mainWindow
!= null
) mainWindow
.pack();
293 if (mainWindow
!= null
) mainWindow
.setVisible(true
);
295 case 4: // drawing tasks
298 String text
= runtime
.cstring(arg2
);
299 if (text
.equals("")) text
= " ";
300 System
.out
.println("status '" + text
+ "'");
301 statusBar
.setText(text
); break;
303 gg
= pp
.backBuffer
.createGraphics();
304 if (arg2
!= 0 || arg3
!= 0) {
305 gg
.setColor(Color
.black
);
306 gg
.fillRect(0, 0, arg2
, getHeight());
307 gg
.fillRect(0, 0, getWidth(), arg3
);
308 gg
.fillRect(getWidth() - arg2
, 0, arg2
, getHeight());
309 gg
.fillRect(0, getHeight() - arg3
, getWidth(), arg3
);
310 gg
.setClip(arg2
, arg3
, getWidth()-2*arg2
, getHeight()-2*arg3
);
313 case 2: gg
.dispose(); pp
.repaint(); break;
314 case 3: gg
.setClip(arg2
, arg3
, xarg1
, xarg2
); break;
316 if (arg2
== 0 && arg3
== 0) {
317 gg
.fillRect(0, 0, getWidth(), getHeight());
319 gg
.setClip(arg2
, arg3
, getWidth()-2*arg2
, getHeight()-2*arg3
);
323 gg
.setColor(colors
[xarg3
]);
324 gg
.fillRect(arg2
, arg3
, xarg1
, xarg2
);
327 gg
.setColor(colors
[xarg3
]);
328 gg
.drawLine(arg2
, arg3
, xarg1
, xarg2
);
331 xPoints
= new int[arg2
];
332 yPoints
= new int[arg2
];
336 gg
.setColor(colors
[arg3
]);
337 gg
.fillPolygon(xPoints
, yPoints
, xPoints
.length
);
339 gg
.setColor(colors
[arg2
]);
340 gg
.drawPolygon(xPoints
, yPoints
, xPoints
.length
);
344 gg
.setColor(colors
[arg3
]);
345 gg
.fillOval(xarg1
-xarg3
, xarg2
-xarg3
, xarg3
*2, xarg3
*2);
347 gg
.setColor(colors
[arg2
]);
348 gg
.drawOval(xarg1
-xarg3
, xarg2
-xarg3
, xarg3
*2, xarg3
*2);
351 for(int i
=0; i
<blitters
.length
; i
++) {
352 if (blitters
[i
] == null
) {
353 blitters
[i
] = new BufferedImage(arg2
, arg3
, BufferedImage
.TYPE_3BYTE_BGR
);
357 throw new RuntimeException("No free blitter found!");
358 case 11: blitters
[arg2
] = null
; break;
360 timer
.start(); break;
365 case 5: // more arguments
370 case 6: // polygon vertex
375 gg
.setColor(colors
[arg2
]);
377 String text
= runtime
.cstring(arg3
);
378 Font ft
= new Font((xarg3
& 0x10) != 0 ?
"Monospaced" : "Dialog",
380 int height100
= this.getFontMetrics(ft
).getHeight();
381 ft
= ft
.deriveFont(arg1
* 100 / (float)height100
);
382 FontMetrics fm
= this.getFontMetrics(ft
);
383 int asc
= fm
.getAscent(), desc
= fm
.getDescent();
384 if ((xarg3
& ALIGN_VCENTRE
) != 0)
385 xarg2
+= asc
- (asc
+desc
)/2;
388 int wid
= fm
.stringWidth(text
);
389 if ((xarg3
& ALIGN_HCENTRE
) != 0)
391 else if ((xarg3
& ALIGN_HRIGHT
) != 0)
394 gg
.drawString(text
, xarg1
, xarg2
);
397 case 8: // blitter_save
398 Graphics g2
= blitters
[arg1
].createGraphics();
399 g2
.drawImage(pp
.backBuffer
, 0, 0, blitters
[arg1
].getWidth(), blitters
[arg1
].getHeight(),
400 arg2
, arg3
, arg2
+ blitters
[arg1
].getWidth(), arg3
+ blitters
[arg1
].getHeight(), this);
403 case 9: // blitter_load
404 gg
.drawImage(blitters
[arg1
], arg2
, arg3
, this);
406 case 10: // dialog_init
407 dlg
= new ConfigDialog(this, runtime
.cstring(arg1
));
409 case 11: // dialog_add_control
415 String name
= runtime
.cstring(xarg3
);
418 dlg
.addTextBox(ptr
, name
, runtime
.cstring(sval_ptr
));
421 dlg
.addCheckBox(ptr
, name
, ival
!= 0);
424 dlg
.addComboBox(ptr
, name
, runtime
.cstring(sval_ptr
), ival
);
432 case 13: // tick a menu item
433 if (arg1
< 0) arg1
= typeMenu
.getItemCount() - 1;
434 for (int i
= 0; i
< typeMenu
.getItemCount(); i
++) {
435 if (typeMenu
.getMenuComponent(i
) instanceof JCheckBoxMenuItem
) {
436 ((JCheckBoxMenuItem
)typeMenu
.getMenuComponent(i
)).setSelected(arg1
== i
);
441 if (cmd
>= 1024 && cmd
< 2048) { // palette
442 colors
[cmd
-1024] = new Color(arg1
, arg2
, arg3
);
445 pp
.setBackground(colors
[0]);
446 if (statusBar
!= null
) statusBar
.setBackground(colors
[0]);
447 this.setBackground(colors
[0]);
451 } catch (Throwable ex
) {
452 ex
.printStackTrace();
458 private void addStatusBar() {
459 statusBar
= new JLabel("test");
460 statusBar
.setBorder(new BevelBorder(BevelBorder
.LOWERED
));
461 getContentPane().add(BorderLayout
.SOUTH
,statusBar
);
465 public static void main(String
[] args
) {
466 final PuzzleApplet a
= new PuzzleApplet();
467 JFrame jf
= new JFrame("Loading...");
468 jf
.getContentPane().setLayout(new BorderLayout());
469 jf
.getContentPane().add(a
, BorderLayout
.CENTER
);
473 jf
.setDefaultCloseOperation(JFrame
.EXIT_ON_CLOSE
);
474 jf
.addWindowListener(new WindowAdapter() {
475 public void windowClosing(WindowEvent e
) {
483 public static class PuzzlePanel
extends JPanel
{
485 private static final long serialVersionUID
= 1L;
486 protected BufferedImage backBuffer
;
488 public PuzzlePanel() {
489 setPreferredSize(new Dimension(100,100));
490 createBackBuffer(100,100, Color
.black
);
493 public void createBackBuffer(int w
, int h
, Color bg
) {
494 if (w
> 0 && h
> 0) {
495 backBuffer
= new BufferedImage(w
,h
, BufferedImage
.TYPE_3BYTE_BGR
);
496 Graphics g
= backBuffer
.createGraphics();
498 g
.fillRect(0, 0, w
, h
);
503 protected void paintComponent(Graphics g
) {
504 g
.drawImage(backBuffer
, 0, 0, this);
508 public static class ConfigComponent
{
510 public int configItemPointer
;
511 public JComponent component
;
513 public ConfigComponent(int type
, int configItemPointer
, JComponent component
) {
515 this.configItemPointer
= configItemPointer
;
516 this.component
= component
;
520 public class ConfigDialog
extends JDialog
{
522 private GridBagConstraints gbcLeft
= new GridBagConstraints(
523 GridBagConstraints
.RELATIVE
, GridBagConstraints
.RELATIVE
, 1, 1,
524 0, 0, GridBagConstraints
.WEST
, GridBagConstraints
.NONE
,
525 new Insets(0, 0, 0, 0), 0, 0);
526 private GridBagConstraints gbcRight
= new GridBagConstraints(
527 GridBagConstraints
.RELATIVE
, GridBagConstraints
.RELATIVE
,
528 GridBagConstraints
.REMAINDER
, 1, 1.0, 0,
529 GridBagConstraints
.CENTER
, GridBagConstraints
.HORIZONTAL
,
530 new Insets(5, 5, 5, 5), 0, 0);
531 private GridBagConstraints gbcBottom
= new GridBagConstraints(
532 GridBagConstraints
.RELATIVE
, GridBagConstraints
.RELATIVE
,
533 GridBagConstraints
.REMAINDER
, GridBagConstraints
.REMAINDER
,
534 1.0, 1.0, GridBagConstraints
.CENTER
,
535 GridBagConstraints
.HORIZONTAL
, new Insets(5, 5, 5, 5), 0, 0);
537 private static final long serialVersionUID
= 1L;
538 private List components
= new ArrayList();
540 public ConfigDialog(JApplet parent
, String title
) {
541 super(JOptionPane
.getFrameForComponent(parent
), title
, true
);
542 getContentPane().setLayout(new GridBagLayout());
545 public void addTextBox(int ptr
, String name
, String value
) {
546 getContentPane().add(new JLabel(name
), gbcLeft
);
547 JComponent c
= new JTextField(value
, 25);
548 getContentPane().add(c
, gbcRight
);
549 components
.add(new ConfigComponent(C_STRING
, ptr
, c
));
553 public void addCheckBox(int ptr
, String name
, boolean selected
) {
554 JComponent c
= new JCheckBox(name
, selected
);
555 getContentPane().add(c
, gbcRight
);
556 components
.add(new ConfigComponent(C_BOOLEAN
, ptr
, c
));
559 public void addComboBox(int ptr
, String name
, String values
, int selected
) {
560 getContentPane().add(new JLabel(name
), gbcLeft
);
561 StringTokenizer st
= new StringTokenizer(values
.substring(1), values
.substring(0,1));
562 JComboBox c
= new JComboBox();
563 c
.setEditable(false
);
564 while(st
.hasMoreTokens())
565 c
.addItem(st
.nextToken());
566 c
.setSelectedIndex(selected
);
567 getContentPane().add(c
, gbcRight
);
568 components
.add(new ConfigComponent(C_CHOICES
, ptr
, c
));
571 public void finish() {
572 JPanel buttons
= new JPanel(new GridLayout(1, 2, 5, 5));
573 getContentPane().add(buttons
, gbcBottom
);
575 buttons
.add(b
=new JButton("OK"));
576 b
.addActionListener(new ActionListener() {
577 public void actionPerformed(ActionEvent e
) {
582 getRootPane().setDefaultButton(b
);
583 buttons
.add(b
=new JButton("Cancel"));
584 b
.addActionListener(new ActionListener() {
585 public void actionPerformed(ActionEvent e
) {
589 setDefaultCloseOperation(DISPOSE_ON_CLOSE
);
591 setLocationRelativeTo(null
);
594 private void save() {
595 for (int i
= 0; i
< components
.size(); i
++) {
596 ConfigComponent cc
= (ConfigComponent
) components
.get(i
);
599 JTextField jtf
= (JTextField
)cc
.component
;
600 runtimeCall("jcallback_config_set_string", new int[] {cc
.configItemPointer
, runtime
.strdup(jtf
.getText())});
603 JCheckBox jcb
= (JCheckBox
)cc
.component
;
604 runtimeCall("jcallback_config_set_boolean", new int[] {cc
.configItemPointer
, jcb
.isSelected()?
1:0});
607 JComboBox jcm
= (JComboBox
)cc
.component
;
608 runtimeCall("jcallback_config_set_boolean", new int[] {cc
.configItemPointer
, jcm
.getSelectedIndex()});
612 runtimeCall("jcallback_config_ok", new int[0]);