Create a read-eval-print-loop in a new top-level window. If
shared
is true, it uses the same environment as the current(interaction-environment)
; if not (the default), a new top-level environment is created.You can create multiple top-level window that can co-exist. They run in separate threads.
Kawa makes it easy to build “rich client” (i.e. GUI) applications using JavaFX. For example the following program will print up a window with a button; clicking on the button will print a message on the console output about the event.
(require 'javafx-defs) (javafx-application) (javafx-scene title: "Hello Button" width: 600 height: 450 (Button text: "Click Me" layout-x: 25 layout-y: 40 on-action: (lambda (e) (format #t "Event: ~s~%~!" e))))
JavaFX support is builtin to the pre-built kawa-1.14.1.jar
.
It is easiest to use JDK 8; see below if you're using JDK 7.
If you build Kawa from source, specify --with-javafx
on the
configure
command line (assuming you're using JDK 8).
Assume the above file is HelloButton1.scm
, you can
run it like this:
$ kawa HelloButton1.scm
For more information and examples read this (slightly older) introduction, and this on animation.
JDK 8 ships with JavaFX, and it is in the default CLASSPATH
.
JDK 7 update 9 or later does have JavaFX included, but it is a separate
jfxrt.jar
which not in the default CLASSPATH
.
Thus have have to eplicitly add jfxrt.jar
.
To run the previous HelloButton1.scm
you can do:
java -cp $JAVA_HOME/lib/jfxrt.jar:$KAWA_HOME/kawa.jar HelloButton1.scm
If you build Kawa from source, do:
$ ./configure --with-javafx=$JAVA_HOME --enable-kawa-frontend ...other-args...
The resulting Kawa binary sets up the path to jfxrt.jar
so you just need to do:
$ kawa HelloButton1.scm
Google's phone/tablet operating system Android is based on a custom virtual machine on top of a Linux kernel. Even though Android isn't strictly (or legally) speaking Java, you can build Android applications using Kawa.
Below is "Hello world" written in Kawa Scheme. A slightly more interesting example is in next section.
(require 'android-defs) (activity hello (on-create-view (android.widget.TextView (this) text: "Hello, Android from Kawa Scheme!")))
The following instructions have been tested on GNU/Linux, specifically Fedora 17. This link may be helpful if you're building on Windows.
First download the Android SDK. Unzip in a suitable location, which we'll refer to as ANDROID_HOME
.
export ANDROID_HOME=/path/to/android-sdk-linux PATH=$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools:$PATH
Next you have to get the appropriate platform SDK:
$ android update sdk
You need to select an Android “platform”.
Platform (API) 16 corresponds to Android 4.1.2 (Jelly Bean).
Select that or whatever you prefer, and click Install
.
(You can install multiple platforms, but each project
is built for a specific platform.)
ANDROID_PLATFORM=android-16
Set JAVA_HOME
to where your JDK tree is.
You should use JDK 6; JDK 7 does not work at time of writing.
$ export JAVA_HOME=/opt/jdk1.6
First get the Kawa source code.
If using Ant (as is recommended on Windows):
$ ant -Denable-android=true
Alternatively, you can use configure
and make
:
$ KAWA_DIR=path_to_Kawa_sources $ cd $KAWA_DIR $ ./configure --with-android=$ANDROID_HOME/platforms/$ANDROID_PLATFORM/android.jar --disable-xquery --disable-jemacs $ make
Next, we need to create a project or “activity”.
This tutorial assumes you want to create the project
in the target directory KawaHello
,
with the main activity being a class named hello
in a
package kawa.android
:
PROJECT_DIR=KawaHello PROJECT_CLASS=hello PROJECT_PACKAGE=kawa.android PROJECT_PACKAGE_PATH=kawa/android
To create the project use the following command:
$ android create project --target $ANDROID_PLATFORM --name $PROJECT_DIR --activity $PROJECT_CLASS --path ./$PROJECT_DIR --package $PROJECT_PACKAGE
Replace the skeleton hello.java
by the Scheme code at the
top of this note, placing in a file named hello.scm
:
$ cd $PROJECT_DIR $ HELLO_APP_DIR=`pwd` $ cd $HELLO_APP_DIR/src/$PROJECT_PACKAGE_PATH $ rm $PROJECT_CLASS.java $ create $PROJECT_CLASS.scm
We need to copy/link the Kawa jar file so the Android SDK can find it:
$ cd $HELLO_APP_DIR $ ln -s $KAWA_DIR/kawa-1.14.1.jar libs/kawa.jar
Optionally, you can use kawart-1.14.1.jar, which is slightly smaller, but does not support eval, and does not get built by the Ant build:
$ ln -s $KAWA_DIR/kawart-1.14.1.jar libs/kawa.jar
Copy or link custom_rules.xml
from the Kawa sources:
ln -s $KAWA_DIR/gnu/kawa/android/custom_rules.xml .
Finally to build the application just do:
$ ant debug
First you need to create an Android Virtual Device (avd). Start:
android
Then from menu Tools
select Manage AVDs...
.
In the new window click New....
Pick a Name
(we use avd16
in the following),
a Target
(to match $ANDROID_PLATFORM
),
and optionally change the other properties, before clicking Create AVD
.
Now you can start up the Android emulator:
$ emulator -avd avd16 &
Wait until Android has finished booting (you will see the Android home screen), click the menu and home buttons. Now install our new application:
adb install bin/KawaHello-debug.apk
If the emulator is running, kill it:
$ kill %emulator
On your phone or other Android devude, enable USB debugging.
(This is settable from the Settings
application,
under Applications / Development
.)
Connect the phone to your computer with the USB cable.
Verify that the phone is accessible to adb
:
$ adb devices List of devices attached 0A3A560F0C015024 device
If you don't see a device listed, it may be permission problem. You can figure out which device corresponds to the phone by doing:
$ ls -l /dev/bus/usb/* /dev/bus/usb/001: total 0 ... crw-rw-rw- 1 root wheel 189, 5 2010-10-18 16:52 006 ...
The timestamp corresponds to when you connected the phone. Make the USB connection readable:
$ sudo chmod a+w /dev/bus/usb/001/006
Obviously if you spend time developing for an Androd phone you'll want to automate this process; this link or this link may be helpful.
Anyway, once adb
can talk to the phone, you install in the same way as before:
adb install bin/KawaHello-debug.apk
You will find a copy of the SDK documentation in $ANDROID_HOME/docs/index.html
.
If the emulator complains that your application has stopped unexpectedly, do:
$ adb logcat
This shows log messages, stack traces, output from the Log.i
logging method, and other useful information.
(You can alternatively start ddms
(Dalvik Debug Monitor Service), click on the kawa.android line
in the top-left sub-window to select it, then from the Device
menu select Run logcat....
).
To uninstall your application, do:
$ adb uninstall kawa.android
(A more interesting text-to-speech example app is on Santosh Rajan's Android-Scheme blog.)
An Android user interface is constructed from View
objects.
The following is an example that illustrates some features of
Kawa to help write views hierarchies,
The example is self-contained, and can be built and run
as described in the section called “Building for Android”.
(require 'android-defs) (activity hello (on-create-view (define counter ::integer 0) (define counter-view (TextView text: "Not clicked yet.")) (LinearLayout orientation: LinearLayout:VERTICAL (TextView text: "Hello, Android from Kawa Scheme!") (Button text: "Click here!" on-click-listener: (lambda (e) (set! counter (+ counter 1)) (counter-view:setText (format "Clicked ~d times." counter)))) counter-view)))
The first import
form imports various useful definitions
from the Kawa Android library. Using these is not required for
writing a Kawa application, but makes it more convenient.
An Android application consists of one or more activities,
each of which is an instance of the android.app.Activity
class.
You can use the activity
macro to define your Activity
class.
The first macro argument (in this case hello
) is the class name,
and the others are members of the class, in the syntax of
a field-or-method-decl
. The sub-form on-create-view
is an abbreviation for declaring an onCreate
method
(which is called when the Activity
starts up
followed by a setContentView
:
The body of the on-create-view
is evaluated.
The result should be a View
expression,
which is passed to setContentView
.
The names LinearLayout
, TextView
, and Button
are just aliases for standard Android View
sub-classes.
A few are prefined by (require 'android-defs)
, or you
can define them yourself using define-alias
.
To create an instance of a View
class you “call” the
class as if it were a function,
as described in the section called “Allocating objects”.
For example:
(TextView (this) text: "Hello, Android from Kawa Scheme!")
If you (require 'android-defs)
that defines
some special handling for View
classes.
You can leave out the (this)
argument,
which refers to the enclosing Activity
:
(TextView text: "Hello, Android from Kawa Scheme!")
You can register event listeners on Android View
objects
using methods typically named setOn
.
For example EVENT
ListenersetOnClickListener
. When allocating
an object you can leave out the set
, and you can optionally
use Scheme-style names: on-click-listener
. The argument
is an object of a special nested listener class,
for example View$OnClickListener
. These are
single-method classes, so you can use a lambda expression
and the section called “Anonymous classes” will automatically create the needed
listener class.
Returns a nonempty list of immutable strings. The first element is an implementation-specific name for the running top-level program. The remaining elements are the command-line arguments, as passed to the
main
method (except for those flags processed by Kawa itself).The first element will depend on how the Kawa module was invoked. Kawa uses the following rules to determine the command name:
If the property
kawa.command.name
is set, that is used. This variable can be set on thekawa
command line, for example from a script:kawa -Dkawa.command.name="$0" foo "$@"This variable is also set implicitly by the meta-arg option. FIXME.
If we're reading a source file that starts with the Unix command-file prefix ‘
#!/
’ then we use the name of the source file. The assumption is that such a file is an executable script.If the Java property
kawa.command.line
is set, then we use that (after stripping off text that duplicates the remaining arguments). Thekawa
program sets this property to the command line used to invoke it (specifically the contents of the entireargv
array), before invoking thejava
program.If the Java property
sun.java.command
is set, then we use that (after stripping off text that duplicates the remaining arguments), and then prepending the string"java "
. The OpenJDKjava
program sets this property.If all else fails, the command name is
"kawa"
.
Any command-line arguments (following flags processed by Kawa itself) are assigned to the global variable ‘
command-line-arguments
’, which is a vector of strings.
process-command-line-assignments
Process any initial command-line options that set variables. These have the form
. Any such command-line options (at the start of the command-line) are processed and removed from the command-line.
name
=value
$ java kawa.repl -- abc=123 def #|kawa:1|# (write (command-line)) ("java kawa.repl --" "abc=123" "def") #|kawa:2|# (process-command-line-assignments) #|kawa:3|# (write (command-line)) ("java kawa.repl -- abc=123" "def") #|kawa:4|# abc 123This function is mostly useful for Kawa applications compiled with the
--main
option. (It is used to set XQueryexternal
variables.)
Many operating systems provide each running process with an environment conisting of environment variables. (This environment is not to be confused with the Scheme environments that can be passed to
eval
.) Both the name and value of an environment variable are strings. The procedureget-environment-variable
returns the value of the environment variablename
, or#f
if the environment variable is not found. (This uses thejava.lang.System:getenv
method.) It is an error to mutate the resulting string.(get-environment-variable "PATH") ⇒ "/usr/local/bin:/usr/bin:/bin"
Returns the names and values of all the environment variables as an alist, where the car of each entry is the name of an environment variable, and the cdr is its value, both as strings. It is an error to mutate any of the strings or the alist itself.
(get-environment-variables) ⇒ (("USER" . "root") ("HOME" . "/"))
Exits the Kawa interpreter, and ends the Java session. Returns the value of
code
to the operating system: Thecode
must be integer, or the special values#f
(equivalent to -1), or#t
(equivalent to 0). Ifcode
is not specified, zero is returned. Thecode
is a status code; by convention a non-zero value indicates a non-standard (error) return.Before exiting, finally-handlers (as in
try-finally
, or theafter
procedure ofdynamic-wind
) are executed, but only in the current thread, and only if the current thread was started normally. (Specifically if we're inside anExitCalled
block with non-zero nesting - seegnu.kawa.util.ExitCalled
.) Also, JVM shutdown hooks are executed - which includes flushing buffers of output ports. (SpecificallyWriter
objects registered with theWriterManager
.)
Exits the Kawa interpreter, and ends the Java session. Communicates an exit value in the same manner as
exit
. Unlikeexit
, neither finally-handlers nor shutdown hooks are executed.
Creates a
<java.lang.Process>
object, using the specifiedcommand
andenvp
. Thecommand
is converted to an array of Java strings (that is an object that has type<java.lang.String[]>
. It can be a Scheme vector or list (whose elements should be Java strings or Scheme strings); a Java array of Java strings; or a Scheme string. In the latter case, the command is converted usingcommand-parse
. Theenvp
is process environment; it should be either a Java array of Java strings, or the special#!null
value.
Runs the specified
command
, and waits for it to finish. Returns the return code from the command. The return code is an integer, where 0 conventionally means successful completion. Thecommand
can be any of the types handled bymake-process
.
The value of this variable should be a one-argument procedure. It is used to convert a command from a Scheme string to a Java array of the constituent "words". The default binding, on Unix-like systems, returns a new command to invoke
"/bin/sh" "-c"
concatenated with the command string; on non-Unix-systems, it is bound totokenize-string-to-string-array
.
tokenize-string-to-string-array
command
Uses a
java.util.StringTokenizer
to parse thecommand
string into an array of words. This splits thecommand
using spaces to delimit words; there is no special processing for quotes or other special characters. (This is the same as whatjava.lang.Runtime.exec(String)
does.)
Returns an inexact number represent the current time on the International Atomic Time (TAI) scale. The value 0.0 represents midnight on January 1, 1070 TAI (equivalent to 10 seconds before midnight Universal Time), and the value 1.0 represents on TAI second later. Neither high acuracy nor high precision are required; in particular returning Coordinated Universal Time plus a suitable constant might be the best an implementation cat do. The Kawa implementation just multiplies by 0.001 the result of calling the method
currentTimeMillis
in classjava.lang.System
.
Returns the number of jiffies as an exact integer that have elapses since an arbitrary implementation-defined epoch (instant). A jiffy is an implementation-defined fraction of a second which is defined by the return value of the
jiffies-per-second
procedure. The starting epoch (instant 0) is guaranteed to be constant during a run of the program, but may vary between runs. (At the time of writing, Kawa's jiffy is one nano-second.)Rationale: Jiffies are allowed to be implementation-dependent so that
current-jiffy
can execute with minimal overhead. It should be very likely that a compactly represented integer will suffice as the return value. Any particular jiffy size will be inappropriate some some implementations: a microsecond is too long for a very fast machine, while a much smaller unit would force many implementations to return integers which have to allocated for most calls, renderingcurrent-jiffy
less useful for accurate timing measurements.
Returns an exact integer representing the number of jiffies per SI second. This value is an implementation-specified constant. (At the time of writing, the value in Kawa is 1,000,000,000.)
These sections document older and less convenient ways to call Java methods, access Java fields, and use Java arrays.
The following lower-level primitives require you to specify
the parameter and return types explicitly.
You should probably use the functions invoke
and invoke-static
(see the section called “Calling Java methods from Scheme”) instead.
primitive-constructor
class
(argtype
...
)
Returns a new anonymous procedure, which when called will create a new object of the specified class, and will then call the constructor matching the specified argument types.
primitive-virtual-method
class
method
rtype
(argtype
...
)
Returns a new anonymous procedure, which when called will invoke the instance method whose name is the string
method
in the class whose name isclass
.
primitive-static-method
class
method
rtype
(argtype
...
)
Returns a new anonymous procedure, which when called will invoke the static method whose name is the string
method
in the class whose name isclass
.
primitive-interface-method
interface
method
rtype
(argtype
...
)
Returns a new anonymous procedure, which when called will invoke the matching method from the interface whose name is
interface
.
The macros return procedure values, just like lambda
.
If the macros are used directly as the procedure of a procedure call,
then kawa can inline the correct bytecodes to call the specified methods.
(Note also that neither macro
checks that there really is a method that matches the specification.)
Otherwise, the Java reflection facility is used.
The following macros evaluate to procedures that can be used to access or change the fields of objects or static fields. The compiler can inline each to a single bytecode instruction (not counting type conversion).
These macros are deprecated.
The fields
and static-field
functions
(see the section called “Accessing object fields”) are easier to use, more powerful, and
just as efficient. However, the high-level functions currently do
not provide access to non-public fields.
primitive-get-field
class
fname
ftype
Use this to access a field named
fname
having typetype
in classclass
. Evaluates to a new one-argument procedure, whose argument is a reference to an object of the specifiedclass
. Calling that procedure returns the value of the specified field.
primitive-set-field
class
fname
ftype
Use this to change a field named
fname
having typetype
in classclass
. Evaluates to a new two-argument procedure, whose first argument is a reference to an object of the specifiedclass
, and the second argument is the new value. Calling that procedure sets the field to the specified value. (This macro's name does not end in a ‘!
’, because it does not actually set the field. Rather, it returns a function for setting the field.)
primitive-get-static
class
fname
ftype
Like
primitive-get-field
, but used to access static fields. Returns a zero-argument function, which when called returns the value of the static field.
The following macros evaluate to procedures that can be used to manipulate primitive Java array objects. The compiler can inline each to a single bytecode instruction (not counting type conversion).
primitive-array-new
element-type
Evaluates to a one-argument procedure. Applying the resulting procedure to an integer count allocates a new Java array of the specified length, and whose elements have type
element-type
.
primitive-array-set
element-type
Evaluates to a three-argument procedure. The first argument of the resulting procedure must be an array whose elements have type
element-type
; the second argument is an index; and the third argument is a value (coercible toelement-type
) which replaces the value specified by the index in the given array.