Add emuname for telling apart multiple emulators
[jpcrr.git] / org / jpc / plugins / LuaPlugin.java
blob527ba968cb05c569b814a670afc1ae85d647a285
1 /*
2 JPC-RR: A x86 PC Hardware Emulator
3 Release 1
5 Copyright (C) 2007-2009 Isis Innovation Limited
6 Copyright (C) 2009-2010 H. Ilari Liusvaara
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License version 2 as published by
10 the Free Software Foundation.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 Based on JPC x86 PC Hardware emulator,
22 A project from the Physics Dept, The University of Oxford
24 Details about original JPC can be found at:
26 www-jpc.physics.ox.ac.uk
30 package org.jpc.plugins;
32 import mnj.lua.*;
34 import java.io.*;
35 import java.util.*;
36 import java.awt.*;
37 import java.awt.event.*;
38 import javax.swing.*;
39 import java.lang.reflect.*;
40 import org.jpc.Misc;
42 import org.jpc.emulator.PC;
43 import org.jpc.emulator.HardwareComponent;
44 import org.jpc.emulator.VGADigitalOut;
45 import org.jpc.pluginsbase.Plugins;
46 import org.jpc.pluginsbase.Plugin;
47 import static org.jpc.Misc.parseStringToComponents;
48 import static org.jpc.Misc.errorDialog;
49 import static org.jpc.Misc.moveWindow;
50 import static org.jpc.Misc.openStream;
52 //Locking this class is used for preventing termination and when terminating.
53 public class LuaPlugin implements ActionListener, Plugin
55 private JFrame window;
56 private JPanel panel;
57 private Plugins vPluginManager;
58 private String kernelName;
59 private Map<String, String> kernelArguments;
60 private Map<String, String> userArguments;
61 private int nativeWidth;
62 private int nativeHeight;
63 private JLabel execLabel;
64 private JTextField execName;
65 private JButton execButton;
66 private JButton termButton;
67 private JButton clearButton;
68 private JTextArea console;
70 private int nextHandle;
72 //luaThread is null if Lua isn't running.
73 private Thread luaThread;
74 private Lua luaState;
75 private volatile boolean pcRunning;
76 private volatile String luaInvokeReq;
77 private volatile boolean luaTerminateReq;
78 private volatile boolean luaTerminateReqAsync;
79 private VGADigitalOut screenOut;
80 private PC pc;
81 private volatile boolean ownsVGALock;
82 private volatile boolean ownsVGALine;
83 private volatile boolean signalComplete;
84 private volatile boolean luaStarted;
85 private volatile boolean mainThreadWait;
86 private volatile boolean reconnectInProgress;
88 private VGARetraceWaiter vgaPoller;
90 private boolean consoleMode;
91 private boolean specialNoGUIMode;
93 private Map<String, LuaResource> resources;
94 private IdentityHashMap<LuaResource, Integer> liveObjects;
96 public static abstract class LuaResource
98 String handle;
99 LuaPlugin plugin;
101 public LuaResource(LuaPlugin _plugin)
103 handle = "h" + (_plugin.nextHandle++);
104 plugin = _plugin;
105 plugin.resources.put(handle, this);
108 public final String getHandle()
110 return handle;
113 public final void release(boolean noExceptions) throws IOException
115 try {
116 destroy();
117 } catch(IOException e) {
118 if(!noExceptions)
119 throw e;
121 plugin.liveObjects.remove(this);
122 plugin.resources.remove(handle);
125 public abstract void destroy() throws IOException;
128 private String getMethodHandle(Lua l)
130 if(l.type(1) == Lua.TNONE) {
131 l.error("Handle required for method call");
132 return null;
134 l.checkType(1, Lua.TUSERDATA);
135 Object _u = l.toUserdata(l.value(1)).getUserdata();
136 if(!(_u instanceof String)) {
137 if(_u != null) {
138 l.error("Invalid handle to resource: " + _u.getClass().getName());
139 } else {
140 l.error("Invalid handle to resource: Null");
142 return null;
144 return (String)_u;
147 public void destroyLuaObject(Lua l) throws IOException
149 String u = getMethodHandle(l);
150 LuaResource r1 = resources.get(u);
151 if(r1 == null) {
152 l.error("Bad or closed handle passed to method");
154 r1.release(false);
157 public boolean systemShutdown()
159 //Just terminate the emulator.
160 return true;
163 public void reconnect(PC _pc)
165 vgaPoller.deactivate();
166 //Get the event waiter out of way.
167 reconnectInProgress = true;
168 if(luaThread != null)
169 luaThread.interrupt();
170 synchronized(this) {
171 reconnectInProgress = false;
172 if(ownsVGALock && screenOut != null) {
173 screenOut.releaseOutput(this);
174 ownsVGALock = false;
176 if(ownsVGALine && screenOut != null) {
177 screenOut.unsubscribeOutput(this);
178 ownsVGALine = false;
180 if(_pc != null) {
181 screenOut = _pc.getVideoOutput();
182 pc = _pc;
183 } else {
184 screenOut = null;
185 pc = null;
187 if(screenOut != null && luaThread != null) {
188 screenOut.subscribeOutput(this);
189 ownsVGALine = true;
190 vgaPoller.reactivate();
193 queueEvent("attach", null);
196 public void pcStarting()
198 pcRunning = true;
201 public void pcStopping()
203 pcRunning = false;
204 queueEvent("stop", null);
207 class LuaCallback extends LuaJavaCallback
209 Method callbackMethod;
210 Object onObject;
212 LuaCallback(Object target, Method callback)
214 onObject = target;
215 callbackMethod = callback;
218 public int luaFunction(Lua l) {
219 synchronized(LuaPlugin.this) {
220 try {
221 if(!liveObjects.containsKey(onObject)) {
222 l.error("Attempted to call method on dead object");
223 return 0;
224 } else
225 return ((Integer)callbackMethod.invoke(onObject, luaState, LuaPlugin.this)).intValue();
226 } catch(InvocationTargetException e) {
227 if(e.getCause() instanceof LuaError)
228 throw (LuaError)e.getCause(); //Pass runtime exceptions through.
229 errorDialog(e.getCause(), "Error in callback", null, "Terminate Lua VM");
230 terminateLuaVMAsync();
231 } catch(Exception e) {
232 if(e instanceof LuaError)
233 throw (LuaError)e; //Pass runtime exceptions through.
234 errorDialog(e, "Error invoking callback", null, "Terminate Lua VM");
235 terminateLuaVMAsync();
238 while(true);
242 public void tableAddFunctions(Lua l, LuaTable table, Object obj, Class<?> clazz)
244 if(obj != null)
245 clazz = obj.getClass();
246 //Add all exported callbacks.
247 Method[] candidateMethods = clazz.getMethods();
248 for(Method candidate: candidateMethods) {
249 if(obj != null && Modifier.isStatic(candidate.getModifiers()))
250 continue; //Want non-static.
251 if(obj == null && !Modifier.isStatic(candidate.getModifiers()))
252 continue; //Want static.
253 if(!Modifier.isPublic(candidate.getModifiers()))
254 continue; //Want public.
255 if(!candidate.getName().startsWith("luaCB_"))
256 continue; //Not this...
257 String luaName = candidate.getName().substring(6);
258 Class<?>[] paramTypes = candidate.getParameterTypes();
259 Class<?> retType = candidate.getReturnType();
260 if(retType != int.class) {
261 System.err.println("Warning: Incorrect return type for " + candidate.getName() +
262 ": " + retType.getName() + ".");
263 continue;
265 if(paramTypes == null || paramTypes.length != 2) {
266 System.err.println("Warning: Incorrect parameter type for " + candidate.getName() + ".");
267 continue;
269 if(paramTypes[0] != Lua.class || paramTypes[1] != LuaPlugin.class) {
270 System.err.println("Warning: Incorrect parameter type for " + candidate.getName() + ".");
271 continue;
274 l.setTable(table, luaName, new LuaCallback(obj, candidate));
278 public LuaUserdata generateLuaClass(Lua l, LuaResource towrap)
280 LuaUserdata user = new LuaUserdata(towrap.getHandle());
281 LuaTable t = l.newTable();
282 tableAddFunctions(l, t, towrap, null);
283 liveObjects.put(towrap, null);
284 l.setTable(t, "__index" , t);
285 l.setMetatable(user, t);
286 l.push(user);
287 return user;
290 class LuaThread implements Runnable
292 Lua lua;
293 String script;
295 LuaThread(Lua _lua, String _script)
297 BaseLib.open(_lua);
298 StringLib.open(_lua);
299 MathLib.open(_lua);
300 TableLib.open(_lua);
301 lua = _lua;
302 script = _script;
305 private String describeFault(int r)
307 if(r == 0) return null;
308 else if(r == Lua.YIELD) return "Main thread yielded.";
309 else if(r == Lua.ERRRUN) return "Unprotected runtime error";
310 else if(r == Lua.ERRSYNTAX) return "syntax error";
311 //else if(r == Lua.ERRMEM) return "Out of memory");
312 else if(r == Lua.ERRFILE) return "I/O error loading";
313 else if(r == Lua.ERRERR) return "Double fault";
314 else return "Unknown fault #" + r;
317 public void run()
319 LuaTable sTable;
321 lua.setGlobal("script", script);
323 lua.setGlobal("args", sTable = lua.newTable());
324 for(Map.Entry<String, String> x : kernelArguments.entrySet())
325 lua.setTable(sTable, x.getKey(), x.getValue());
326 for(Map.Entry<String, String> x : userArguments.entrySet())
327 lua.setTable(sTable, "x-" + x.getKey(), x.getValue());
329 tableAddFunctions(lua, lua.getGlobals(), null, LuaPlugin.class);
331 //Wait for lua startup to be signaled in order to avoid deadlocks.
332 while(!luaStarted)
333 try {
334 wait();
335 } catch(Exception e) {
338 InputStream kernel = null;
339 try {
340 kernel = new BufferedInputStream(openStream(kernelName, "datafiles/luakernel"));
341 int r = lua.load(kernel, "Kernel");
342 String fault = describeFault(r);
343 if(fault != null)
344 throw new Exception("Kernel loading error: " + fault);
345 r = lua.pcall(0, 0, null);
346 fault = describeFault(r);
347 if(fault != null)
348 throw new Exception("Kernel error: " + fault);
349 } catch(Exception e) {
350 printConsoleMsg("\n\nLua Error: " + e.getMessage() + "\n" +
351 lua.value(-1).toString() + "\n\n");
352 //e.printStackTrace();
353 errorDialog(e, "Lua error", null, "Dismiss");
355 //Lua script quit. Terminate the VM.
356 synchronized(LuaPlugin.this) {
357 cleanupLuaResources();
358 luaThread = null;
359 luaState = null;
360 LuaPlugin.this.notifyAll();
362 printConsoleMsg("Lua VM: Lua script finished.\n");
366 private void cleanupLuaResources()
368 vgaPoller.deactivate();
369 if(ownsVGALock) {
370 screenOut.releaseOutput(LuaPlugin.this);
371 ownsVGALock = false;
373 if(ownsVGALine) {
374 screenOut.unsubscribeOutput(LuaPlugin.this);
375 ownsVGALine = false;
378 while(resources.size() > 0) {
379 Map.Entry<String, LuaResource> entry = resources.entrySet().iterator().next();
380 String key = entry.getKey();
381 LuaResource obj = entry.getValue();
382 try {
383 obj.release(true);
384 } catch(Exception e) {
386 resources.remove(key);
387 liveObjects.remove(obj);
389 resources.clear();
392 public void main()
394 while(true) {
395 try {
396 synchronized(this) {
397 mainThreadWait = true;
398 notifyAll();
399 if(luaInvokeReq == null && !luaTerminateReq)
400 wait();
401 mainThreadWait = false;
403 } catch(Exception e) {
404 continue;
406 if(luaInvokeReq != null && luaThread == null) {
407 //Run the Lua VM.
408 eventQueue = new LinkedList<Event>();
409 if(screenOut != null && !ownsVGALine) {
410 screenOut.subscribeOutput(this);
411 ownsVGALine = true;
412 vgaPoller.reactivate();
414 luaStarted = false;
415 luaState = new Lua();
416 luaThread = new Thread(new LuaThread(luaState, luaInvokeReq), "Lua execution thread");
417 luaThread.start();
418 synchronized(this) {
419 luaInvokeReq = null;
420 signalComplete = true;
421 luaStarted = true;
422 notifyAll();
424 } else if(luaInvokeReq != null) {
425 //Invoke request with Lua running? Shouldn't happen.
426 System.err.println("Error: Lua invoke request with Lua running!");
427 luaInvokeReq = null;
428 } else if(luaTerminateReq && luaThread != null) {
429 //This is fun... Terminate Lua VM. Sychronize in order to avoid terminating VM in
430 //inapporiate place. And yes, that thread gets killed! The interrupt is to prevent
431 //or kick the object from sleeping on VGA wait.
432 luaThread.interrupt();
433 synchronized(this) {
434 luaThread.stop();
435 cleanupLuaResources();
436 luaState = null;
437 luaThread = null;
438 luaTerminateReq = false;
439 signalComplete = true;
440 notifyAll();
441 if(luaTerminateReqAsync)
442 setLuaButtons();
444 printConsoleMsg("Lua VM: Lua VM terminated.\n");
445 cleanupLuaResources();
446 } else if(luaTerminateReq) {
447 //Invoke request with Lua running? Shouldn't happen.
448 System.err.println("Error: Lua terminate request with Lua not running!");
449 luaTerminateReq = false;
450 setLuaButtons();
451 } else {
452 setLuaButtons();
457 public void printConsoleMsg(String msg)
459 final String _msg = msg;
461 if(consoleMode || specialNoGUIMode) {
462 System.out.print(msg);
463 return;
466 if(!SwingUtilities.isEventDispatchThread())
467 try {
468 //Do this async to avoid deadlocks with PCRunner stop.
469 SwingUtilities.invokeLater(new Thread() { public void run() {
470 console.setText(console.getText() + _msg);
471 }});
472 } catch(Exception e) {
474 else
475 console.setText(console.getText() + msg);
478 private synchronized void invokeLuaVM(String script) throws Exception
480 if(luaThread != null) {
481 return;
484 int split = script.indexOf('(');
485 if(split < 0) {
486 userArguments = new HashMap<String, String>();
487 } else {
488 String arguments = script.substring(split + 1);
489 int split2 = arguments.lastIndexOf(')');
490 if(split2 != arguments.length() - 1)
491 throw new Exception("Bad argument syntax");
492 arguments = arguments.substring(0, split2);
493 userArguments = parseStringToComponents(arguments);
496 //Starting from Lua itself is not possible.
497 signalComplete = false;
498 if(split < 0)
499 luaInvokeReq = script;
500 else
501 luaInvokeReq = script.substring(0, split);
502 luaTerminateReq = false;
503 notifyAll();
504 while(!signalComplete)
505 try {
506 wait();
507 } catch(Exception e) {
510 setLuaButtons();
513 private synchronized void terminateLuaVM()
515 notifyAll();
516 if(luaThread == null)
517 return;
519 //This request won't go to lua execution thread.
520 signalComplete = false;
521 luaTerminateReq = true;
522 luaTerminateReqAsync = false;
523 notifyAll();
524 while(luaThread != null)
525 try {
526 wait();
527 } catch(Exception e) {
530 setLuaButtons();
533 private synchronized void terminateLuaVMAsync()
535 if(luaThread == null)
536 return;
538 //This request won't go to lua execution thread.
539 signalComplete = false;
540 luaTerminateReq = true;
541 luaTerminateReqAsync = true;
542 notifyAll();
545 private void setLuaButtons()
547 if(consoleMode || specialNoGUIMode)
548 return;
550 if(!SwingUtilities.isEventDispatchThread())
551 try {
552 SwingUtilities.invokeAndWait(new Thread() { public void run() {
553 LuaPlugin.this.execButton.setText((luaThread == null) ? "Run" : "Send");
554 LuaPlugin.this.termButton.setEnabled(luaThread != null);
555 }});
556 } catch(Exception e) {
558 else {
559 LuaPlugin.this.execButton.setText((luaThread == null) ? "Run" : "Send");
560 LuaPlugin.this.termButton.setEnabled(luaThread != null);
564 private void clearConsole()
566 if(consoleMode || specialNoGUIMode)
567 return;
569 if(!SwingUtilities.isEventDispatchThread())
570 try {
571 SwingUtilities.invokeAndWait(new Thread() { public void run() {
572 console.setText("");
573 }});
574 } catch(Exception e) {
576 else {
577 console.setText("");
581 public void actionPerformed(ActionEvent evt)
583 if(evt.getSource() == execButton) {
584 try {
585 if(luaThread == null)
586 invokeLuaVM(execName.getText());
587 else
588 postMessage(execName.getText());
589 } catch(Exception e) {
590 printConsoleMsg("Lua script starting / message send error: " + e.getMessage());
592 } else if(evt.getSource() == termButton) {
593 luaTerminateReq = true;
594 if(luaThread != null)
595 luaThread.interrupt();
596 terminateLuaVM();
597 } else if(evt.getSource() == clearButton) {
598 clearConsole();
602 public void eci_luaplugin_sendmessage(String x)
604 if(luaThread != null && x != null)
605 postMessage(x);
608 public void eci_luaplugin_setwinpos(Integer x, Integer y)
610 moveWindow(window, x.intValue(), y.intValue(), nativeWidth, nativeHeight);
613 public void eci_luaplugin_run(String script)
615 if(luaThread == null)
616 try {
617 invokeLuaVM(script);
618 } catch(Exception e) {
619 printConsoleMsg("Lua script starting error: " + e.getMessage());
623 public void eci_luaplugin_terminate()
625 luaTerminateReq = true;
626 if(luaThread != null)
627 luaThread.interrupt();
628 terminateLuaVMAsync();
631 public void eci_luaplugin_clearconsole()
633 clearConsole();
636 private void invokeCommand(String cmd, String[] args)
638 if("luaplugin-terminate".equals(cmd) && args == null && luaThread != null) {
639 luaTerminateReq = true;
640 if(luaThread != null)
641 luaThread.interrupt();
642 terminateLuaVMAsync();
646 public void callInvokeCommand(String cmd, String[] args, boolean sync)
648 if(consoleMode)
649 invokeCommand(cmd, args);
650 else if(sync)
651 vPluginManager.invokeExternalCommandSynchronous(cmd, args);
652 else
653 vPluginManager.invokeExternalCommand(cmd, args);
656 public Object[] callCommand(String cmd, String[] args)
658 if(consoleMode) {
659 invokeCommand(cmd, args);
660 return null;
662 return vPluginManager.invokeExternalCommandReturn(cmd, args);
665 public HardwareComponent getComponent(Class<?> clazz)
667 if(pc == null)
668 return null;
669 return pc.getComponent(clazz);
672 public void postMessage(String msg)
674 queueEvent("message", msg);
677 public void doReleaseVGA()
679 if(screenOut != null && ownsVGALine && ownsVGALock)
680 screenOut.releaseOutput(this);
681 ownsVGALock = false;
682 vgaPoller.reactivate();
685 public boolean getOwnsVGALock()
687 return ownsVGALock;
690 public int getXResolution()
692 if(screenOut != null && ownsVGALine)
693 return screenOut.getWidth();
694 return -1;
697 public int getYResolution()
699 if(screenOut != null && ownsVGALine)
700 return screenOut.getHeight();
701 return -1;
704 public boolean getPCConnected()
706 return (screenOut != null);
709 public boolean getPCRunning()
711 return pcRunning;
714 class VGARetraceWaiter extends Thread
716 private volatile boolean active;
717 private volatile boolean reactivateFlag;
718 private volatile boolean deactivateFlag;
720 public VGARetraceWaiter()
722 super("VGA Lua Trace waiting thread");
725 public void run()
727 while(true) {
728 synchronized(this) {
729 if(!active || !ownsVGALine) {
730 //We are in quescent state. Wait for reactivation.
731 active = false;
732 while(!reactivateFlag)
733 try {
734 wait();
735 } catch(Exception e) {
737 active = true;
738 reactivateFlag = false;
739 } else {
740 boolean r = screenOut.waitOutput(LuaPlugin.this);
741 if(r) {
742 ownsVGALock = true;
743 queueEvent("lock", null);
744 active = false;
746 if(deactivateFlag) {
747 active = false;
748 deactivateFlag = false;
755 public void deactivate()
757 if(!active)
758 return;
759 deactivateFlag = true;
760 interrupt();
761 while(true) {
762 synchronized(this) {
763 if(!active)
764 return;
765 try {
766 wait();
767 } catch(Exception e) {
773 public void reactivate()
775 if(active)
776 return;
777 reactivateFlag = true;
778 synchronized(this) {
779 notifyAll();
784 public void queueEvent(String type, String data)
786 Event e = new Event();
787 e.type = type;
788 e.data = data;
789 synchronized(eventQueue) {
790 eventQueue.offer(e);
791 eventQueue.notifyAll();
795 public Event pollEvent()
797 synchronized(eventQueue) {
798 return eventQueue.poll();
802 public Event waitEvent()
804 synchronized(eventQueue) {
805 Event e = null;
806 while((e = eventQueue.poll()) == null && !luaTerminateReq && !reconnectInProgress)
807 try {
808 eventQueue.wait();
809 } catch(Exception f) {
810 return null;
812 return e;
816 public class Event
818 public String type;
819 public String data;
822 private Queue<Event> eventQueue;
824 public LuaPlugin(String args) throws Exception
826 kernelArguments = parseStringToComponents(args);
827 userArguments = new HashMap<String, String>();
828 kernelName = kernelArguments.get("kernel");
829 kernelArguments.remove("kernel");
831 vgaPoller = new VGARetraceWaiter();
832 vgaPoller.start();
834 if(kernelArguments.get("noguimode") != null)
835 this.specialNoGUIMode = true;
837 this.pcRunning = false;
838 this.luaThread = null;
839 this.luaInvokeReq = null;
840 this.luaTerminateReq = false;
841 this.consoleMode = true;
843 this.resources = new HashMap<String, LuaResource>();
844 this.liveObjects = new IdentityHashMap<LuaResource, Integer>();
845 this.eventQueue = new LinkedList<Event>();
846 liveObjects.put(null, null); //NULL is always considered live.
849 public LuaPlugin(Plugins manager, String args) throws Exception
851 this(args);
853 this.consoleMode = false;
854 this.vPluginManager = manager;
856 if(specialNoGUIMode)
857 return;
859 window = new JFrame("Lua window" + Misc.emuname);
860 GridBagLayout layout = new GridBagLayout();
861 GridBagConstraints c = new GridBagConstraints();
862 panel = new JPanel(layout);
863 window.add(panel);
865 console = new JTextArea(25, 80);
866 console.setFont(new Font("Monospaced", Font.PLAIN, 12));
867 JScrollPane consoleScroller = new JScrollPane(console);
868 console.setEditable(false);
869 c.fill = GridBagConstraints.BOTH;
870 c.gridwidth = 5;
871 c.gridx = 0;
872 c.gridy = 0;
873 c.weighty = 1;
874 panel.add(consoleScroller, c);
876 execLabel = new JLabel("Lua script");
877 c.fill = GridBagConstraints.NONE;
878 c.weightx = 0;
879 c.weighty = 0;
880 c.gridwidth = 1;
881 c.gridx = 0;
882 c.gridy = 1;
883 panel.add(execLabel, c);
885 execName = new JTextField("", 40);
886 c.fill = GridBagConstraints.HORIZONTAL;
887 c.weightx = 1;
888 c.gridwidth = 1;
889 c.gridx = 1;
890 c.gridy = 1;
891 panel.add(execName, c);
893 execButton = new JButton("Run");
894 c.fill = GridBagConstraints.NONE;
895 c.weightx = 0;
896 c.gridwidth = 1;
897 c.gridx = 2;
898 c.gridy = 1;
899 panel.add(execButton, c);
900 execButton.addActionListener(this);
902 termButton = new JButton("Terminate Lua VM");
903 c.fill = GridBagConstraints.NONE;
904 c.weightx = 0;
905 c.gridwidth = 1;
906 c.gridx = 3;
907 c.gridy = 1;
908 panel.add(termButton, c);
909 termButton.addActionListener(this);
910 termButton.setEnabled(false);
912 clearButton = new JButton("Clear Console");
913 c.fill = GridBagConstraints.NONE;
914 c.weightx = 0;
915 c.gridwidth = 1;
916 c.gridx = 4;
917 c.gridy = 1;
918 panel.add(clearButton, c);
919 clearButton.addActionListener(this);
921 window.pack();
922 window.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
923 Dimension d = window.getSize();
924 nativeWidth = d.width;
925 nativeHeight = d.height;
926 window.setVisible(true);
929 class DedicatedShutdownHandler extends Thread
931 public void run()
933 terminateLuaVM();
937 class RunMainThread implements Runnable
939 public void run()
941 main();
945 public static void main(String[] args)
947 if(args.length != 2) {
948 System.err.println("Syntax: LuaPlugin <script> <args>");
949 return;
953 LuaPlugin p;
954 try {
955 p = new LuaPlugin(args[1]);
956 } catch(Exception e) {
957 System.err.println("Can't initialize LuaPlugin: " + e.getMessage());
958 e.printStackTrace();
959 return;
962 Runtime.getRuntime().addShutdownHook(p.new DedicatedShutdownHandler());
963 Thread mThread = new Thread(p.new RunMainThread(), "Lua execution thread");
964 mThread.start();
966 synchronized(p) {
967 //Wait for main thread to become ready and send invoke request.
968 while(!p.mainThreadWait)
969 try {
970 p.wait();
971 } catch(Exception e) {
973 try {
974 p.invokeLuaVM(args[0]);
975 } catch(Exception e) {
976 System.err.println("Error: Can't start Lua VM: " + e.getMessage());
979 //Wait for lua VM to finish.
980 while(p.luaThread != null || p.luaInvokeReq != null)
981 try {
982 p.wait();
983 } catch(Exception e) {
986 mThread.stop();
989 //Some extremely important callbacks.
990 public static int luaCB_print_console_msg(Lua l, LuaPlugin plugin)
992 if(l.type(1) != Lua.TSTRING) {
993 l.error("Unexpected types to print_console_msg");
994 return 0;
996 plugin.printConsoleMsg(l.value(1).toString() + "\n");
997 return 0;
1000 public static int luaCB_loadmodule(Lua l, LuaPlugin plugin)
1002 if(l.type(1) != Lua.TSTRING) {
1003 l.error("Unexpected types to loadmodule");
1004 return 0;
1006 try {
1007 Class<?> clazz = Class.forName(l.checkString(1));
1008 LuaTable tab = l.newTable();
1009 plugin.tableAddFunctions(l, tab, null, clazz);
1010 l.push(tab);
1011 } catch(Exception e) {
1012 l.error("No such extension module: " + l.checkString(1));
1013 return 0;
1015 return 1;