chiark / gitweb /
Michael Schierl's patch to compile the puzzles as Java applets using
authorSimon Tatham <anakin@pobox.com>
Tue, 10 Jun 2008 20:35:17 +0000 (20:35 +0000)
committerSimon Tatham <anakin@pobox.com>
Tue, 10 Jun 2008 20:35:17 +0000 (20:35 +0000)
NestedVM. Wow!

[originally from svn r8064]

LICENCE
PuzzleApplet.java [new file with mode: 0644]
Recipe
mkfiles.pl
nestedvm.c [new file with mode: 0644]
no-icon.c
puzzles.but

diff --git a/LICENCE b/LICENCE
index 43a7a291203ebf05409b0f394d216bc91664c6ca..551cffd20d31680b0ca5cd66a8ae2a1fdb309ae5 100644 (file)
--- a/LICENCE
+++ b/LICENCE
@@ -1,7 +1,7 @@
 This software is copyright (c) 2004-2008 Simon Tatham.
 
 Portions copyright Richard Boulton, James Harvey, Mike Pinna, Jonas
-Kölker and Dariusz Olszewski.
+Kölker, Dariusz Olszewski and Michael Schierl.
 
 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation files
diff --git a/PuzzleApplet.java b/PuzzleApplet.java
new file mode 100644 (file)
index 0000000..4c9928d
--- /dev/null
@@ -0,0 +1,598 @@
+/*
+ * PuzzleApplet.java: NestedVM applet for the puzzle collection
+ */
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.BufferedImage;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.border.BevelBorder;
+import javax.swing.Timer;
+import java.util.List;
+
+import org.ibex.nestedvm.Runtime;
+
+public class PuzzleApplet extends JApplet implements Runtime.CallJavaCB {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final int CFG_SETTINGS = 0, CFG_SEED = 1, CFG_DESC = 2,
+            LEFT_BUTTON = 0x0200, MIDDLE_BUTTON = 0x201, RIGHT_BUTTON = 0x202,
+            LEFT_DRAG = 0x203, MIDDLE_DRAG = 0x204, RIGHT_DRAG = 0x205,
+            LEFT_RELEASE = 0x206, CURSOR_UP = 0x209, CURSOR_DOWN = 0x20a,
+            CURSOR_LEFT = 0x20b, CURSOR_RIGHT = 0x20c, MOD_CTRL = 0x1000,
+            MOD_SHFT = 0x2000, MOD_NUM_KEYPAD = 0x4000, ALIGN_VCENTRE = 0x100,
+            ALIGN_HCENTRE = 0x001, ALIGN_HRIGHT = 0x002, C_STRING = 0,
+            C_CHOICES = 1, C_BOOLEAN = 2;
+
+    private JFrame mainWindow;
+
+    private JMenu typeMenu;
+    private JMenuItem solveCommand;
+    private Color[] colors;
+    private JLabel statusBar;
+    private PuzzlePanel pp;
+    private Runtime runtime;
+    private Graphics2D  gg;
+    private Timer timer;
+    private int xarg1, xarg2, xarg3;
+    private int[] xPoints, yPoints;
+    private BufferedImage[] blitters = new BufferedImage[512];
+    private ConfigDialog dlg;
+
+    static {
+        try {
+            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    public void init() {
+        try {
+            Container cp = getContentPane();
+            cp.setLayout(new BorderLayout());
+            runtime = (Runtime) Class.forName("PuzzleEngine").newInstance();
+            runtime.setCallJavaCB(this);
+            JMenuBar menubar = new JMenuBar();
+            JMenu jm;
+            menubar.add(jm = new JMenu("Game"));
+            addMenuItemWithKey(jm, "New", 'n');
+            addMenuItemCallback(jm, "Restart", "jcallback_restart_event");
+            addMenuItemCallback(jm, "Specific...", "jcallback_config_event", CFG_DESC);
+            addMenuItemCallback(jm, "Random Seed...", "jcallback_config_event", CFG_SEED);
+            jm.addSeparator();
+            addMenuItemWithKey(jm, "Undo", 'u');
+            addMenuItemWithKey(jm, "Redo", 'r');
+            jm.addSeparator();
+            solveCommand = addMenuItemCallback(jm, "Solve", "jcallback_solve_event");
+            solveCommand.setEnabled(false);
+            if (mainWindow != null) {
+                jm.addSeparator();
+                addMenuItemWithKey(jm, "Exit", 'q');
+            }
+            menubar.add(typeMenu = new JMenu("Type"));
+            typeMenu.setVisible(false);
+            menubar.add(jm = new JMenu("Help"));
+            addMenuItemCallback(jm, "About", "jcallback_about_event");
+            setJMenuBar(menubar);
+            cp.add(pp = new PuzzlePanel(), BorderLayout.CENTER);
+            pp.addKeyListener(new KeyAdapter() {
+                public void keyPressed(KeyEvent e) {
+                    int key = -1;
+                    int shift = e.isShiftDown() ? MOD_SHFT : 0;
+                    int ctrl = e.isControlDown() ? MOD_CTRL : 0;
+                    switch (e.getKeyCode()) {
+                    case KeyEvent.VK_LEFT:
+                    case KeyEvent.VK_KP_LEFT:
+                        key = shift | ctrl | CURSOR_LEFT;
+                        break;
+                    case KeyEvent.VK_RIGHT:
+                    case KeyEvent.VK_KP_RIGHT:
+                        key = shift | ctrl | CURSOR_RIGHT;
+                        break;
+                    case KeyEvent.VK_UP:
+                    case KeyEvent.VK_KP_UP:
+                        key = shift | ctrl | CURSOR_UP;
+                        break;
+                    case KeyEvent.VK_DOWN:
+                    case KeyEvent.VK_KP_DOWN:
+                        key = shift | ctrl | CURSOR_DOWN;
+                        break;
+                    case KeyEvent.VK_PAGE_UP:
+                        key = shift | ctrl | MOD_NUM_KEYPAD | '9';
+                        break;
+                    case KeyEvent.VK_PAGE_DOWN:
+                        key = shift | ctrl | MOD_NUM_KEYPAD | '3';
+                        break;
+                    case KeyEvent.VK_HOME:
+                        key = shift | ctrl | MOD_NUM_KEYPAD | '7';
+                        break;
+                    case KeyEvent.VK_END:
+                        key = shift | ctrl | MOD_NUM_KEYPAD | '1';
+                        break;
+                    default:
+                        if (e.getKeyCode() >= KeyEvent.VK_NUMPAD0 && e.getKeyCode() <=KeyEvent.VK_NUMPAD9) {
+                            key = MOD_NUM_KEYPAD | (e.getKeyCode() - KeyEvent.VK_NUMPAD0+'0');
+                        }
+                    break;
+                    }
+                    if (key != -1) {
+                        runtimeCall("jcallback_key_event", new int[] {0, 0, key});
+                    }
+                }
+                public void keyTyped(KeyEvent e) {
+                    runtimeCall("jcallback_key_event", new int[] {0, 0, e.getKeyChar()});
+                }
+            });
+            pp.addMouseListener(new MouseAdapter() {
+                public void mouseReleased(MouseEvent e) {
+                    mousePressedReleased(e, true);
+                }
+                public void mousePressed(MouseEvent e) {
+                    pp.requestFocus();
+                    mousePressedReleased(e, false);
+                }
+                private void mousePressedReleased(MouseEvent e, boolean released) {
+                    int button;
+                    if ((e.getModifiers() & (InputEvent.BUTTON2_MASK | InputEvent.SHIFT_MASK)) != 0)
+                        button = MIDDLE_BUTTON;
+                    else if ((e.getModifiers() & (InputEvent.BUTTON3_MASK | InputEvent.ALT_MASK)) != 0)
+                        button = RIGHT_BUTTON;
+                    else if ((e.getModifiers() & (InputEvent.BUTTON1_MASK)) != 0)
+                        button = LEFT_BUTTON;
+                    else
+                        return;
+                    if (released)
+                        button += LEFT_RELEASE - LEFT_BUTTON;
+                    runtimeCall("jcallback_key_event", new int[] {e.getX(), e.getY(), button});
+                }
+            });
+            pp.addMouseMotionListener(new MouseMotionAdapter() {
+                public void mouseDragged(MouseEvent e) {
+                    int button;
+                    if ((e.getModifiers() & (InputEvent.BUTTON2_MASK | InputEvent.SHIFT_MASK)) != 0)
+                        button = MIDDLE_DRAG;
+                    else if ((e.getModifiers() & (InputEvent.BUTTON3_MASK | InputEvent.ALT_MASK)) != 0)
+                        button = RIGHT_DRAG;
+                    else
+                        button = LEFT_DRAG;
+                    runtimeCall("jcallback_key_event", new int[] {e.getX(), e.getY(), button});
+                }
+            });
+            pp.addComponentListener(new ComponentAdapter() {
+                public void componentResized(ComponentEvent e) {
+                    handleResized();
+                }
+            });
+            pp.setFocusable(true);
+            pp.requestFocus();
+            timer = new Timer(20, new ActionListener() {
+                public void actionPerformed(ActionEvent e) {
+                    runtimeCall("jcallback_timer_func", new int[0]);
+                }
+            });
+            SwingUtilities.invokeLater(new Runnable() {
+                public void run() {
+                    runtime.start();
+                    runtime.execute();
+                }
+            });
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    public void destroy() {
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                runtime.execute();
+                if (mainWindow != null) {
+                    mainWindow.dispose();
+                    System.exit(0);
+                }
+            }
+        });
+    }
+
+    protected void handleResized() {
+        pp.createBackBuffer(pp.getWidth(), pp.getHeight(), colors[0]);
+        runtimeCall("jcallback_resize", new int[] {pp.getWidth(), pp.getHeight()});
+    }
+
+    private void addMenuItemWithKey(JMenu jm, String name, int key) {
+        addMenuItemCallback(jm, name, "jcallback_menu_key_event", key);
+    }
+
+    private JMenuItem addMenuItemCallback(JMenu jm, String name, final String callback, final int arg) {
+        return addMenuItemCallback(jm, name, callback, new int[] {arg});
+    }
+
+    private JMenuItem addMenuItemCallback(JMenu jm, String name, final String callback) {
+        return addMenuItemCallback(jm, name, callback, new int[0]);
+    }
+
+    private JMenuItem addMenuItemCallback(JMenu jm, String name, final String callback, final int[] args) {
+        JMenuItem jmi;
+        if (jm == typeMenu)
+            typeMenu.add(jmi = new JCheckBoxMenuItem(name));
+        else
+        jm.add(jmi = new JMenuItem(name));
+        jmi.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                runtimeCall(callback, args);
+            }
+        });
+        return jmi;
+    }
+
+    protected void runtimeCall(String func, int[] args) {
+        if (runtimeCallWithResult(func, args) == 42 && mainWindow != null) {
+            destroy();
+        }
+    }
+
+    protected int runtimeCallWithResult(String func, int[] args) {
+        try {
+            return runtime.call(func, args);
+        } catch (Runtime.CallException ex) {
+            ex.printStackTrace();
+            return 42;
+        }
+    }
+
+    private void buildConfigureMenuItem() {
+        if (typeMenu.isVisible()) {
+            typeMenu.addSeparator();
+        } else {
+            typeMenu.setVisible(true);
+        }
+        addMenuItemCallback(typeMenu, "Custom...", "jcallback_config_event", CFG_SETTINGS);
+    }
+
+    private void addTypeItem(String name, final int ptrGameParams) {
+        typeMenu.setVisible(true);
+        addMenuItemCallback(typeMenu, name, "jcallback_preset_event", ptrGameParams);
+    }
+
+    public int call(int cmd, int arg1, int arg2, int arg3) {
+        try {
+            switch(cmd) {
+            case 0: // initialize
+                if (mainWindow != null) mainWindow.setTitle(runtime.cstring(arg1));
+                if ((arg2 & 1) != 0) buildConfigureMenuItem();
+                if ((arg2 & 2) != 0) addStatusBar();
+                if ((arg2 & 4) != 0) solveCommand.setEnabled(true);
+                colors = new Color[arg3];
+                return 0;
+            case 1: // Type menu item
+                addTypeItem(runtime.cstring(arg1), arg2);
+                return 0;
+            case 2: // MessageBox
+                JOptionPane.showMessageDialog(this, runtime.cstring(arg2), runtime.cstring(arg1), arg3 == 0 ? JOptionPane.INFORMATION_MESSAGE : JOptionPane.ERROR_MESSAGE);
+                return 0;
+            case 3: // Resize
+                pp.setPreferredSize(new Dimension(arg1, arg2));
+                if (mainWindow != null) mainWindow.pack();
+                handleResized();
+                if (mainWindow != null) mainWindow.setVisible(true);
+                return 0;
+            case 4: // drawing tasks
+                switch(arg1) {
+                case 0:
+                   String text = runtime.cstring(arg2);
+                   if (text.equals("")) text = " ";
+                   System.out.println("status '" + text + "'");
+                   statusBar.setText(text); break;
+                case 1:
+                    gg = pp.backBuffer.createGraphics();
+                    if (arg2 != 0 || arg3 != 0) {
+                        gg.setColor(Color.black);
+                        gg.fillRect(0, 0, arg2, getHeight());
+                        gg.fillRect(0, 0, getWidth(), arg3);
+                        gg.fillRect(getWidth() - arg2, 0, arg2, getHeight());
+                        gg.fillRect(0, getHeight() - arg3, getWidth(), arg3);
+                        gg.setClip(arg2, arg3, getWidth()-2*arg2, getHeight()-2*arg3);
+                    }
+                    break;
+                case 2: gg.dispose(); pp.repaint(); break;
+                case 3: gg.setClip(arg2, arg3, xarg1, xarg2); break;
+                case 4:
+                    if (arg2 == 0 && arg3 == 0) {
+                        gg.fillRect(0, 0, getWidth(), getHeight());
+                    } else {
+                        gg.setClip(arg2, arg3, getWidth()-2*arg2, getHeight()-2*arg3);
+                    }
+                    break;
+                case 5:
+                    gg.setColor(colors[xarg3]);
+                    gg.fillRect(arg2, arg3, xarg1, xarg2);
+                    break;
+                case 6:
+                    gg.setColor(colors[xarg3]);
+                    gg.drawLine(arg2, arg3, xarg1, xarg2);
+                    break;
+                case 7:
+                    xPoints = new int[arg2];
+                    yPoints = new int[arg2];
+                    break;
+                case 8:
+                    if (arg3 != -1) {
+                        gg.setColor(colors[arg3]);
+                        gg.fillPolygon(xPoints, yPoints, xPoints.length);
+                    }
+                    gg.setColor(colors[arg2]);
+                    gg.drawPolygon(xPoints, yPoints, xPoints.length);
+                    break;
+                case 9:
+                    if (arg3 != -1) {
+                        gg.setColor(colors[arg3]);
+                        gg.fillOval(xarg1-xarg3, xarg2-xarg3, xarg3*2, xarg3*2);
+                    }
+                    gg.setColor(colors[arg2]);
+                    gg.drawOval(xarg1-xarg3, xarg2-xarg3, xarg3*2, xarg3*2);
+                    break;
+                case 10:
+                    for(int i=0; i<blitters.length; i++) {
+                        if (blitters[i] == null) {
+                            blitters[i] = new BufferedImage(arg2, arg3, BufferedImage.TYPE_3BYTE_BGR);
+                            return i;
+                        }
+                    }
+                    throw new RuntimeException("No free blitter found!");
+                case 11: blitters[arg2] = null; break;
+                case 12:
+                    timer.start(); break;
+                case 13:
+                    timer.stop(); break;
+                }
+                return 0;
+            case 5: // more arguments
+                xarg1 = arg1;
+                xarg2 = arg2;
+                xarg3 = arg3;
+                return 0;
+            case 6: // polygon vertex
+                xPoints[arg1]=arg2;
+                yPoints[arg1]=arg3;
+                return 0;
+            case 7: // string
+                gg.setColor(colors[arg2]);
+                {
+                    String text = runtime.cstring(arg3);
+                    Font ft = new Font((xarg3 & 0x10) != 0 ? "Monospaced" : "Dialog",
+                            Font.PLAIN, 100);
+                    int height100 = this.getFontMetrics(ft).getHeight();
+                    ft = ft.deriveFont(arg1 * 100 / (float)height100);
+                    FontMetrics fm = this.getFontMetrics(ft);
+                    int asc = fm.getAscent(), desc = fm.getDescent();
+                    if ((xarg3 & ALIGN_VCENTRE) != 0)
+                        xarg2 += asc - (asc+desc)/2;
+                    else
+                        xarg2 += asc;
+                    int wid = fm.stringWidth(text);
+                    if ((xarg3 & ALIGN_HCENTRE) != 0)
+                        xarg1 -= wid / 2;
+                    else if ((xarg3 & ALIGN_HRIGHT) != 0)
+                        xarg1 -= wid;
+                    gg.setFont(ft);
+                    gg.drawString(text, xarg1, xarg2);
+                }
+                return 0;
+            case 8: // blitter_save
+                Graphics g2 = blitters[arg1].createGraphics();
+                g2.drawImage(pp.backBuffer, 0, 0, blitters[arg1].getWidth(), blitters[arg1].getHeight(),
+                        arg2, arg3, arg2 + blitters[arg1].getWidth(), arg3 + blitters[arg1].getHeight(), this);
+                g2.dispose();
+                return 0;
+            case 9: // blitter_load
+                gg.drawImage(blitters[arg1], arg2, arg3, this);
+                return 0;
+            case 10: // dialog_init
+                dlg= new ConfigDialog(this, runtime.cstring(arg1));
+                return 0;
+            case 11: // dialog_add_control
+                {
+                    int sval_ptr = arg1;
+                    int ival = arg2;
+                    int ptr = xarg1;
+                    int type=xarg2;
+                    String name = runtime.cstring(xarg3);
+                    switch(type) {
+                    case C_STRING:
+                        dlg.addTextBox(ptr, name, runtime.cstring(sval_ptr));
+                        break;
+                    case C_BOOLEAN:
+                        dlg.addCheckBox(ptr, name, ival != 0);
+                        break;
+                    case C_CHOICES:
+                        dlg.addComboBox(ptr, name, runtime.cstring(sval_ptr), ival);
+                    }
+                }
+                return 0;
+            case 12:
+                dlg.finish();
+                dlg = null;
+                return 0;
+            case 13: // tick a menu item
+                if (arg1 < 0) arg1 = typeMenu.getItemCount() - 1;
+                for (int i = 0; i < typeMenu.getItemCount(); i++) {
+                    if (typeMenu.getMenuComponent(i) instanceof JCheckBoxMenuItem) {
+                        ((JCheckBoxMenuItem)typeMenu.getMenuComponent(i)).setSelected(arg1 == i);
+                    }
+                }
+                return 0;
+            default:
+                if (cmd >= 1024 && cmd < 2048) { // palette
+                    colors[cmd-1024] = new Color(arg1, arg2, arg3);
+                }
+            if (cmd == 1024) {
+                pp.setBackground(colors[0]);
+                if (statusBar != null) statusBar.setBackground(colors[0]);
+                this.setBackground(colors[0]);
+            }
+            return 0;
+            }
+        } catch (Throwable ex) {
+            ex.printStackTrace();
+            System.exit(-1);
+            return 0;
+        }
+    }
+
+    private void addStatusBar() {
+        statusBar = new JLabel("test");
+        statusBar.setBorder(new BevelBorder(BevelBorder.LOWERED));
+        getContentPane().add(BorderLayout.SOUTH,statusBar);
+    }
+
+    // Standalone runner
+    public static void main(String[] args) {
+        final PuzzleApplet a = new PuzzleApplet();
+        JFrame jf = new JFrame("Loading...");
+        jf.getContentPane().setLayout(new BorderLayout());
+        jf.getContentPane().add(a, BorderLayout.CENTER);
+        a.mainWindow=jf;
+        a.init();
+        a.start();
+        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        jf.addWindowListener(new WindowAdapter() {
+            public void windowClosing(WindowEvent e) {
+                a.stop();
+                a.destroy();
+            }
+        });
+        jf.setVisible(true);
+    }
+
+    public static class PuzzlePanel extends JPanel {
+
+        private static final long serialVersionUID = 1L;
+        protected BufferedImage backBuffer;
+
+        public PuzzlePanel() {
+            setPreferredSize(new Dimension(100,100));
+            createBackBuffer(100,100, Color.black);
+        }
+
+        public void createBackBuffer(int w, int h, Color bg) {
+            backBuffer = new BufferedImage(w,h, BufferedImage.TYPE_3BYTE_BGR);
+            Graphics g = backBuffer.createGraphics();
+            g.setColor(bg);
+            g.fillRect(0, 0, w, h);
+            g.dispose();
+        }
+
+        protected void paintComponent(Graphics g) {
+            g.drawImage(backBuffer, 0, 0, this);
+        }
+    }
+
+    public static class ConfigComponent {
+        public int type;
+        public int configItemPointer;
+        public JComponent component;
+
+        public ConfigComponent(int type, int configItemPointer, JComponent component) {
+            this.type = type;
+            this.configItemPointer = configItemPointer;
+            this.component = component;
+        }
+    }
+
+    public class ConfigDialog extends JDialog {
+
+        private GridBagConstraints gbcLeft = new GridBagConstraints(
+                GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE, 1, 1,
+                0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE,
+                new Insets(0, 0, 0, 0), 0, 0);
+        private GridBagConstraints gbcRight = new GridBagConstraints(
+                GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE,
+                GridBagConstraints.REMAINDER, 1, 1.0, 0,
+                GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+                new Insets(5, 5, 5, 5), 0, 0);
+        private GridBagConstraints gbcBottom = new GridBagConstraints(
+                GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE,
+                GridBagConstraints.REMAINDER, GridBagConstraints.REMAINDER,
+                1.0, 1.0, GridBagConstraints.CENTER,
+                GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 0, 0);
+
+        private static final long serialVersionUID = 1L;
+        private List components = new ArrayList();
+
+        public ConfigDialog(JApplet parent, String title) {
+            super(JOptionPane.getFrameForComponent(parent), title, true);
+            getContentPane().setLayout(new GridBagLayout());
+        }
+
+        public void addTextBox(int ptr, String name, String value) {
+            getContentPane().add(new JLabel(name), gbcLeft);
+            JComponent c = new JTextField(value, 25);
+            getContentPane().add(c, gbcRight);
+            components.add(new ConfigComponent(C_STRING, ptr, c));
+        }
+
+
+        public void addCheckBox(int ptr, String name, boolean selected) {
+            JComponent c = new JCheckBox(name, selected);
+            getContentPane().add(c, gbcRight);
+            components.add(new ConfigComponent(C_BOOLEAN, ptr, c));
+        }
+
+        public void addComboBox(int ptr, String name, String values, int selected) {
+            getContentPane().add(new JLabel(name), gbcLeft);
+            StringTokenizer st = new StringTokenizer(values.substring(1), values.substring(0,1));
+            JComboBox c = new JComboBox();
+            c.setEditable(false);
+            while(st.hasMoreTokens())
+                c.addItem(st.nextToken());
+            c.setSelectedIndex(selected);
+            getContentPane().add(c, gbcRight);
+            components.add(new ConfigComponent(C_CHOICES, ptr, c));
+        }
+
+        public void finish() {
+            JPanel buttons = new JPanel(new GridLayout(1, 2, 5, 5));
+            getContentPane().add(buttons, gbcBottom);
+            JButton b;
+            buttons.add(b=new JButton("OK"));
+            b.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent e) {
+                    save();
+                    dispose();
+                }
+            });
+            getRootPane().setDefaultButton(b);
+            buttons.add(b=new JButton("Cancel"));
+            b.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent e) {
+                    dispose();
+                }
+            });
+            setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+            pack();
+            setLocationRelativeTo(null);
+            setVisible(true);
+        }
+        private void save() {
+            for (int i = 0; i < components.size(); i++) {
+                ConfigComponent cc = (ConfigComponent) components.get(i);
+                switch(cc.type) {
+                case C_STRING:
+                    JTextField jtf = (JTextField)cc.component;
+                    runtimeCall("jcallback_config_set_string", new int[] {cc.configItemPointer, runtime.strdup(jtf.getText())});
+                    break;
+                case C_BOOLEAN:
+                    JCheckBox jcb = (JCheckBox)cc.component;
+                    runtimeCall("jcallback_config_set_boolean", new int[] {cc.configItemPointer, jcb.isSelected()?1:0});
+                    break;
+                case C_CHOICES:
+                    JComboBox jcm = (JComboBox)cc.component;
+                    runtimeCall("jcallback_config_set_boolean", new int[] {cc.configItemPointer, jcm.getSelectedIndex()});
+                    break;
+                }
+            }
+            runtimeCall("jcallback_config_ok", new int[0]);
+        }
+    }
+}
diff --git a/Recipe b/Recipe
index 2decb2fa9aba476dd1fbef68fc7564c16659b2fb..668aac631f15c9d7e56e2f94e7f4e6b69d4bf5d9 100644 (file)
--- a/Recipe
+++ b/Recipe
@@ -13,6 +13,7 @@
 !makefile wce Makefile.wce
 !makefile cygwin Makefile.cyg
 !makefile osx Makefile.osx
+!makefile nestedvm Makefile.nestedvm
 
 !srcdir icons/
 
@@ -115,6 +116,18 @@ FORCE:
        fi
 !end
 !specialobj gtk version
+!begin nestedvm
+version.o: FORCE;
+FORCE:
+       if test -z "$(VER)" && test -f manifest && md5sum -c manifest; then \
+               $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) `cat version.def` -c version.c; \
+       elif test -z "$(VER)" && test -d .svn && svnversion . >/dev/null 2>&1; then \
+               $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) "-DREVISION=`svnversion .`" -c version.c; \
+       else \
+               $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) $(VER) -c version.c; \
+       fi
+!end
+!specialobj nestedvm version
 # For OS X, this is made more fiddly by the fact that we don't have
 # md5sum readily available. We do, however, have `md5 -r' which
 # generates _nearly_ the same output, but it has no check function.
@@ -148,3 +161,26 @@ install:
                || exit 1; \
        done
 !end
+!begin nestedvm
+.PRECIOUS: %.class
+%.class: %.mips
+       java -cp $(NESTEDVM)/build:$(NESTEDVM)/upstream/build/classgen/build \
+               org.ibex.nestedvm.Compiler -outformat class -d . \
+               PuzzleEngine $<
+               mv PuzzleEngine.class $@
+
+org:
+       mkdir -p org/ibex/nestedvm/util
+       cp $(NESTEDVM)/build/org/ibex/nestedvm/{Registers,UsermodeConstants,Runtime*}.class org/ibex/nestedvm
+       cp $(NESTEDVM)/build/org/ibex/nestedvm/util/{Platform*,Seekable*}.class org/ibex/nestedvm/util
+       echo "Main-Class: PuzzleApplet" >applet.manifest
+
+PuzzleApplet.class: PuzzleApplet.java org
+       javac -source 1.3 -target 1.3 PuzzleApplet.java
+
+%.jar: %.class PuzzleApplet.class org
+       mv $< PuzzleEngine.class
+       jar cfm $@ applet.manifest PuzzleEngine.class PuzzleApplet*.class org
+       echo '<applet archive="'$@'" code="PuzzleApplet" width="700" height="500"></applet>' >$*.html
+       mv PuzzleEngine.class $<
+!end
index 376bf461edcbf8971a46d247b6ac226dc2573c65..8119f6c88c001514c8016dcca2270353ef34f9c4 100755 (executable)
@@ -284,7 +284,7 @@ sub mfval($) {
     # Returns true if the argument is a known makefile type. Otherwise,
     # prints a warning and returns false;
     if (grep { $type eq $_ }
-       ("vc","vcproj","cygwin","borland","lcc","gtk","mpw","osx","wce")) {
+       ("vc","vcproj","cygwin","borland","lcc","gtk","mpw","nestedvm","osx","wce")) {
            return 1;
        }
     warn "$.:unknown makefile type '$type'\n";
@@ -1348,6 +1348,59 @@ if (defined $makefiles{'lcc'}) {
     select STDOUT; close OUT;
 }
 
+if (defined $makefiles{'nestedvm'}) {
+    $mftyp = 'nestedvm';
+    $dirpfx = &dirpfx($makefiles{'nestedvm'}, "/");
+
+    ##-- NestedVM makefile
+    open OUT, ">$makefiles{'nestedvm'}"; select OUT;
+    print
+    "# Makefile for $project_name under NestedVM.\n".
+    "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".
+    "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";
+    # gcc command line option is -D not /D
+    ($_ = $help) =~ s/=\/D/=-D/gs;
+    print $_;
+    print
+    "\n".
+    "# This path points at the nestedvm root directory\n".
+    "NESTEDVM = /opt/nestedvm\n".
+    "# You can define this path to point at your tools if you need to\n".
+    "TOOLPATH = \$(NESTEDVM)/upstream/install/bin\n".
+    "CC = \$(TOOLPATH)/mips-unknown-elf-gcc\n".
+    "\n".
+    &splitline("CFLAGS = -O2 -Wall -Werror -DSLOW_SYSTEM -g " .
+              (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n".
+    "\n";
+    print &splitline("all:" . join "", map { " $_.jar" } &progrealnames("X"));
+    print "\n\n";
+    foreach $p (&prognames("X")) {
+      ($prog, $type) = split ",", $p;
+      $objstr = &objects($p, "X.o", undef, undef);
+      $objstr =~ s/gtk\.o/nestedvm\.o/g;
+      print &splitline($prog . ".mips: " . $objstr), "\n";
+      $libstr = &objects($p, undef, undef, "-lX");
+      print &splitline("\t\$(CC)" . $mw . " \$(${type}LDFLAGS) -o \$@ " .
+                       $objstr . " $libstr -lm", 69), "\n\n";
+    }
+    foreach $d (&deps("X.o", undef, $dirpfx, "/")) {
+      $oobjs = $d->{obj};
+      $ddeps= join " ", @{$d->{deps}};
+      $oobjs =~ s/gtk/nestedvm/g;
+      $ddeps =~ s/gtk/nestedvm/g;
+      print &splitline(sprintf("%s: %s", $oobjs, $ddeps)),
+          "\n";
+      $deflist = join "", map { " -D$_" } @{$d->{defs}};
+      print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist" .
+         " -c \$< -o \$\@\n";
+    }
+    print "\n";
+    print $makefile_extra{'nestedvm'};
+    print "\nclean:\n".
+    "\trm -rf *.o *.mips *.class *.html *.jar org applet.manifest\n";
+    select STDOUT; close OUT;
+}
+
 if (defined $makefiles{'osx'}) {
     $mftyp = 'osx';
     $dirpfx = &dirpfx($makefiles{'osx'}, "/");
diff --git a/nestedvm.c b/nestedvm.c
new file mode 100644 (file)
index 0000000..0a23a30
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * nestedvm.c: NestedVM front end for my puzzle collection.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/time.h>
+
+#include "puzzles.h"
+
+extern void _pause();
+extern int _call_java(int cmd, int arg1, int arg2, int arg3);
+
+void fatal(char *fmt, ...)
+{
+    va_list ap;
+    fprintf(stderr, "fatal error: ");
+    va_start(ap, fmt);
+    vfprintf(stderr, fmt, ap);
+    va_end(ap);
+    fprintf(stderr, "\n");
+    exit(1);
+}
+
+struct frontend {
+    // TODO kill unneeded members!
+    midend *me;
+    int timer_active;
+    struct timeval last_time;
+    config_item *cfg;
+    int cfg_which, cfgret;
+    int ox, oy;
+};
+
+static frontend *_fe;
+
+void get_random_seed(void **randseed, int *randseedsize)
+{
+    struct timeval *tvp = snew(struct timeval);
+    gettimeofday(tvp, NULL);
+    *randseed = (void *)tvp;
+    *randseedsize = sizeof(struct timeval);
+}
+
+void frontend_default_colour(frontend *fe, float *output)
+{
+    output[0] = output[1]= output[2] = 0.8f;
+}
+
+void nestedvm_status_bar(void *handle, char *text)
+{
+    _call_java(4,0,(int)text,0);
+}
+
+void nestedvm_start_draw(void *handle)
+{
+    frontend *fe = (frontend *)handle;
+    _call_java(4, 1, fe->ox, fe->oy);
+}
+
+void nestedvm_clip(void *handle, int x, int y, int w, int h)
+{
+    frontend *fe = (frontend *)handle;
+    _call_java(5, w, h, 0);
+    _call_java(4, 3, x + fe->ox, y + fe->oy);
+}
+
+void nestedvm_unclip(void *handle)
+{
+    frontend *fe = (frontend *)handle;
+    _call_java(4, 4, fe->ox, fe->oy);
+}
+
+void nestedvm_draw_text(void *handle, int x, int y, int fonttype, int fontsize,
+                  int align, int colour, char *text)
+{
+    frontend *fe = (frontend *)handle;
+    _call_java(5, x + fe->ox, y + fe->oy, 
+              (fonttype == FONT_FIXED ? 0x10 : 0x0) | align);
+    _call_java(7, fontsize, colour, (int)text);
+}
+
+void nestedvm_draw_rect(void *handle, int x, int y, int w, int h, int colour)
+{
+    frontend *fe = (frontend *)handle;
+    _call_java(5, w, h, colour);
+    _call_java(4, 5, x + fe->ox, y + fe->oy);
+}
+
+void nestedvm_draw_line(void *handle, int x1, int y1, int x2, int y2, 
+                       int colour)
+{
+    frontend *fe = (frontend *)handle;
+    _call_java(5, x2 + fe->ox, y2 + fe->oy, colour);
+    _call_java(4, 6, x1 + fe->ox, y1 + fe->oy);
+}
+
+void nestedvm_draw_poly(void *handle, int *coords, int npoints,
+                       int fillcolour, int outlinecolour)
+{
+    frontend *fe = (frontend *)handle;
+    int i;
+    _call_java(4, 7, npoints, 0);
+    for (i = 0; i < npoints; i++) {
+       _call_java(6, i, coords[i*2] + fe->ox, coords[i*2+1] + fe->oy);
+    }
+    _call_java(4, 8, outlinecolour, fillcolour);
+}
+
+void nestedvm_draw_circle(void *handle, int cx, int cy, int radius,
+                    int fillcolour, int outlinecolour)
+{
+    frontend *fe = (frontend *)handle;
+    _call_java(5, cx+fe->ox, cy+fe->oy, radius);
+    _call_java(4, 9, outlinecolour, fillcolour);
+}
+
+struct blitter {
+    int handle, w, h, x, y;
+};
+
+blitter *nestedvm_blitter_new(void *handle, int w, int h)
+{
+    blitter *bl = snew(blitter);
+    bl->handle = -1;
+    bl->w = w;
+    bl->h = h;
+    return bl;
+}
+
+void nestedvm_blitter_free(void *handle, blitter *bl)
+{
+    if (bl->handle != -1)
+       _call_java(4, 11, bl->handle, 0);
+    sfree(bl);
+}
+
+void nestedvm_blitter_save(void *handle, blitter *bl, int x, int y)
+{
+    frontend *fe = (frontend *)handle;    
+    if (bl->handle == -1)
+       bl->handle = _call_java(4,10,bl->w, bl->h);
+    bl->x = x;
+    bl->y = y;
+    _call_java(8, bl->handle, x + fe->ox, y + fe->oy);
+}
+
+void nestedvm_blitter_load(void *handle, blitter *bl, int x, int y)
+{
+    frontend *fe = (frontend *)handle;
+    assert(bl->handle != -1);
+    if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) {
+        x = bl->x;
+        y = bl->y;
+    }
+    _call_java(9, bl->handle, x + fe->ox, y + fe->oy);
+}
+
+void nestedvm_end_draw(void *handle)
+{
+    _call_java(4,2,0,0);
+}
+
+const struct drawing_api nestedvm_drawing = {
+    nestedvm_draw_text,
+    nestedvm_draw_rect,
+    nestedvm_draw_line,
+    nestedvm_draw_poly,
+    nestedvm_draw_circle,
+    NULL, // draw_update,
+    nestedvm_clip,
+    nestedvm_unclip,
+    nestedvm_start_draw,
+    nestedvm_end_draw,
+    nestedvm_status_bar,
+    nestedvm_blitter_new,
+    nestedvm_blitter_free,
+    nestedvm_blitter_save,
+    nestedvm_blitter_load,
+    NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */
+    NULL,                             /* line_width */
+};
+
+int jcallback_key_event(int x, int y, int keyval)
+{
+    frontend *fe = (frontend *)_fe;
+    if (fe->ox == -1)
+        return 1;
+    if (keyval >= 0 &&
+        !midend_process_key(fe->me, x - fe->ox, y - fe->oy, keyval))
+       return 42;
+    return 1;
+}
+
+int jcallback_resize(int width, int height)
+{
+    frontend *fe = (frontend *)_fe;
+    int x, y;
+    x = width;
+    y = height;
+    midend_size(fe->me, &x, &y, TRUE);
+    fe->ox = (width - x) / 2;
+    fe->oy = (height - y) / 2;
+    midend_force_redraw(fe->me);
+    return 0;
+}
+
+int jcallback_timer_func()
+{
+    frontend *fe = (frontend *)_fe;
+    if (fe->timer_active) {
+       struct timeval now;
+       float elapsed;
+       gettimeofday(&now, NULL);
+       elapsed = ((now.tv_usec - fe->last_time.tv_usec) * 0.000001F +
+                  (now.tv_sec - fe->last_time.tv_sec));
+        midend_timer(fe->me, elapsed); /* may clear timer_active */
+       fe->last_time = now;
+    }
+    return fe->timer_active;
+}
+
+void deactivate_timer(frontend *fe)
+{
+    if (fe->timer_active)
+       _call_java(4, 13, 0, 0);
+    fe->timer_active = FALSE;
+}
+
+void activate_timer(frontend *fe)
+{
+    if (!fe->timer_active) {
+       _call_java(4, 12, 0, 0);
+       gettimeofday(&fe->last_time, NULL);
+    }
+    fe->timer_active = TRUE;
+}
+
+void jcallback_config_ok()
+{
+    frontend *fe = (frontend *)_fe;
+    char *err;
+
+    err = midend_set_config(fe->me, fe->cfg_which, fe->cfg);
+
+    if (err)
+       _call_java(2, (int) "Error", (int)err, 1);
+    else {
+       fe->cfgret = TRUE;
+    }
+}
+
+void jcallback_config_set_string(int item_ptr, int char_ptr) {
+    config_item *i = (config_item *)item_ptr;
+    char* newval = (char*) char_ptr;
+    sfree(i->sval);
+    i->sval = dupstr(newval);
+    free(newval);
+}
+
+void jcallback_config_set_boolean(int item_ptr, int selected) {
+    config_item *i = (config_item *)item_ptr;
+    i->ival = selected != 0 ? TRUE : FALSE;
+}
+
+void jcallback_config_set_choice(int item_ptr, int selected) {
+    config_item *i = (config_item *)item_ptr;
+    i->ival = selected;
+}
+
+static int get_config(frontend *fe, int which)
+{
+    char *title;
+    config_item *i;
+    fe->cfg = midend_get_config(fe->me, which, &title);
+    fe->cfg_which = which;
+    fe->cfgret = FALSE;
+    _call_java(10, (int)title, 0, 0);
+    for (i = fe->cfg; i->type != C_END; i++) {
+       _call_java(5, (int)i, i->type, (int)i->name);
+       _call_java(11, (int)i->sval, i->ival, 0);
+    }
+    _call_java(12,0,0,0);
+    free_cfg(fe->cfg);
+    return fe->cfgret;
+}
+
+int jcallback_menu_key_event(int key)
+{
+    frontend *fe = (frontend *)_fe;
+    if (!midend_process_key(fe->me, 0, 0, key))
+       return 42;
+    return 0;
+}
+
+static void resize_fe(frontend *fe)
+{
+    int x, y;
+
+    x = INT_MAX;
+    y = INT_MAX;
+    midend_size(fe->me, &x, &y, FALSE);
+    _call_java(3, x, y, 0);
+}
+
+int jcallback_preset_event(int ptr_game_params)
+{
+    frontend *fe = (frontend *)_fe;
+    game_params *params =
+       (game_params *)ptr_game_params;
+
+    midend_set_params(fe->me, params);
+    midend_new_game(fe->me);
+    resize_fe(fe);
+    _call_java(13, midend_which_preset(fe->me), 0, 0);
+    return 0;
+}
+
+int jcallback_solve_event()
+{
+    frontend *fe = (frontend *)_fe;
+    char *msg;
+
+    msg = midend_solve(fe->me);
+
+    if (msg)
+       _call_java(2, (int) "Error", (int)msg, 1);
+    return 0;
+}
+
+int jcallback_restart_event()
+{
+    frontend *fe = (frontend *)_fe;
+
+    midend_restart_game(fe->me);
+    return 0;
+}
+
+int jcallback_config_event(int which)
+{
+    frontend *fe = (frontend *)_fe;
+    _call_java(13, midend_which_preset(fe->me), 0, 0);
+    if (!get_config(fe, which))
+       return 0;
+    midend_new_game(fe->me);
+    resize_fe(fe);
+    _call_java(13, midend_which_preset(fe->me), 0, 0);
+    return 0;
+}
+
+int jcallback_about_event()
+{
+    char titlebuf[256];
+    char textbuf[1024];
+
+    sprintf(titlebuf, "About %.200s", thegame.name);
+    sprintf(textbuf,
+           "%.200s\n\n"
+           "from Simon Tatham's Portable Puzzle Collection\n\n"
+           "%.500s", thegame.name, ver);
+    _call_java(2, (int)&titlebuf, (int)&textbuf, 0);
+    return 0;
+}
+
+int main(int argc, char **argv)
+{
+    int i, n;
+    float* colours;
+
+    _fe = snew(frontend);
+    _fe->timer_active = FALSE;
+    _fe->me = midend_new(_fe, &thegame, &nestedvm_drawing, _fe);
+    midend_new_game(_fe->me);
+
+    if ((n = midend_num_presets(_fe->me)) > 0) {
+        int i;
+        for (i = 0; i < n; i++) {
+            char *name;
+            game_params *params;
+            midend_fetch_preset(_fe->me, i, &name, &params);
+           _call_java(1, (int)name, (int)params, 0);
+        }
+    }
+
+    colours = midend_colours(_fe->me, &n);
+    _fe->ox = -1;
+
+    _call_java(0, (int)thegame.name,
+              (thegame.can_configure ? 1 : 0) |
+              (midend_wants_statusbar(_fe->me) ? 2 : 0) |
+              (thegame.can_solve ? 4 : 0), n);    
+    for (i = 0; i < n; i++) {
+       _call_java(1024+ i,
+                  (int)(colours[i*3] * 0xFF),
+                  (int)(colours[i*3+1] * 0xFF),
+                  (int)(colours[i*3+2] * 0xFF));
+    }
+    resize_fe(_fe);
+
+    _call_java(13, midend_which_preset(_fe->me), 0, 0);
+
+    // Now pause the vm. The VM will be call()ed when
+    // an input event occurs.
+    _pause();
+
+    // shut down when the VM is resumed.
+    deactivate_timer(_fe);
+    midend_free(_fe->me);
+    return 0;
+}
index 8a1de91566d9d8917e99ef108ae7934443897d8c..114b2c57c708b9fd98a04c2d6b60c6555bbad9c0 100644 (file)
--- a/no-icon.c
+++ b/no-icon.c
@@ -1,4 +1,3 @@
-#include <X11/Intrinsic.h>
 
 /*
  * Dummy source file which replaces the files generated in the
index 1e9e714af474589ebe45a52d5eb8662d8b45eed4..5c231df3de4308c48b8d9a3a30f481348316d547 100644 (file)
@@ -2271,8 +2271,8 @@ grid, through the \q{Type} menu.
 
 This software is \i{copyright} 2004-2008 Simon Tatham.
 
-Portions copyright Richard Boulton, James Harvey, Mike Pinna and
-Jonas K\u00F6{oe}lker.
+Portions copyright Richard Boulton, James Harvey, Mike Pinna, Jonas
+K\u00F6{oe}lker, Dariusz Olszewski and Michael Schierl.
 
 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation files