From 2c9308077cfcf702a04a37b7d633e912f05e3aee Mon Sep 17 00:00:00 2001 From: simon Date: Tue, 10 Jun 2008 20:35:17 +0000 Subject: [PATCH] Michael Schierl's patch to compile the puzzles as Java applets using NestedVM. Wow! git-svn-id: svn://svn.tartarus.org/sgt/puzzles@8064 cda61777-01e9-0310-a592-d414129be87e --- LICENCE | 2 +- PuzzleApplet.java | 598 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ Recipe | 36 ++++ mkfiles.pl | 55 ++++- nestedvm.c | 416 +++++++++++++++++++++++++++++++++++++ no-icon.c | 1 - puzzles.but | 4 +- 7 files changed, 1107 insertions(+), 5 deletions(-) create mode 100644 PuzzleApplet.java create mode 100644 nestedvm.c diff --git a/LICENCE b/LICENCE index 43a7a29..551cffd 100644 --- 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 index 0000000..4c9928d --- /dev/null +++ b/PuzzleApplet.java @@ -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= 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 2decb2f..668aac6 100644 --- 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 '' >$*.html + mv PuzzleEngine.class $< +!end diff --git a/mkfiles.pl b/mkfiles.pl index 376bf46..8119f6c 100755 --- a/mkfiles.pl +++ b/mkfiles.pl @@ -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 index 0000000..0a23a30 --- /dev/null +++ b/nestedvm.c @@ -0,0 +1,416 @@ +/* + * nestedvm.c: NestedVM front end for my puzzle collection. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#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, ¶ms); + _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; +} diff --git a/no-icon.c b/no-icon.c index 8a1de91..114b2c5 100644 --- a/no-icon.c +++ b/no-icon.c @@ -1,4 +1,3 @@ -#include /* * Dummy source file which replaces the files generated in the diff --git a/puzzles.but b/puzzles.but index 1e9e714..5c231df 100644 --- a/puzzles.but +++ b/puzzles.but @@ -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 -- 2.11.0