diff --git a/build.sbt b/build.sbt index a5e16d2..a0ac69f 100644 --- a/build.sbt +++ b/build.sbt @@ -29,7 +29,7 @@ libraryDependencies ++= Seq( "com.typesafe.scala-logging" %% "scala-logging" % "3.9.2", "com.github.pureconfig" %% "pureconfig" % "0.14.1", "org.rogach" %% "scallop" % "4.0.2", - "org.gphoto" % "gphoto2-java" % "1.5", + "net.java.dev.jna" % "jna" % "4.2.2", "org.typelevel" %% "cats-effect" % "2.3.3", "org.scalatest" %% "scalatest" % "3.2.5" % Test ) diff --git a/src/main/scala/hu/szigyi/ettl/hal/GCamera.scala b/src/main/scala/hu/szigyi/ettl/hal/GCamera.scala index 0e4ec81..275d1c8 100644 --- a/src/main/scala/hu/szigyi/ettl/hal/GCamera.scala +++ b/src/main/scala/hu/szigyi/ettl/hal/GCamera.scala @@ -1,7 +1,7 @@ package hu.szigyi.ettl.hal import GFile.saveTo -import org.gphoto2.{CameraFileMod, CameraMod, CameraWidgets} +import org.gphoto2.{CameraFile, Camera, CameraWidgets} import java.nio.file.Path import scala.util.Try @@ -13,7 +13,7 @@ trait GCamera { } class GCameraImpl extends GCamera { - private val c = new CameraMod + private val c = new Camera override def initialize: Try[Unit] = Try(c.initialize()) @@ -63,7 +63,7 @@ object GFile { } } -class GFileImpl(f: CameraFileMod) extends GFile { +class GFileImpl(f: CameraFile) extends GFile { override def close: Try[Unit] = Try(f.close()) override def saveImageTo(imageBasePath: Path): Try[Path] = diff --git a/src/main/scala/org/gphoto2/CameraMod.java b/src/main/scala/org/gphoto2/Camera.java similarity index 95% rename from src/main/scala/org/gphoto2/CameraMod.java rename to src/main/scala/org/gphoto2/Camera.java index 70be574..8e80bf2 100644 --- a/src/main/scala/org/gphoto2/CameraMod.java +++ b/src/main/scala/org/gphoto2/Camera.java @@ -23,20 +23,21 @@ import org.gphoto2.jna.GPhoto2Native; import org.gphoto2.jna.GPhoto2Native.CameraFilePath; +import java.io.Closeable; import java.io.IOException; /** * Represents a camera. Thread-unsafe. * @author Martin Vysny */ -public class CameraMod extends Camera { +public class Camera implements Closeable { final Pointer camera; /** * Creates a reference to the first connected camera. */ - public CameraMod() { + public Camera() { final PointerByReference ref = new PointerByReference(); CameraUtils.check(GPhoto2Native.INSTANCE.gp_camera_new(ref), "gp_camera_new"); camera = ref.getValue(); @@ -97,10 +98,10 @@ private void checkNotClosed() { * Captures a quick preview image on the camera. * @return camera file, never null. Must be closed afterwards. */ - public CameraFileMod capturePreview() { + public CameraFile capturePreview() { checkNotClosed(); boolean returnedOk = false; - final CameraFileMod cfile = new CameraFileMod(); + final CameraFile cfile = new CameraFile(); try { CameraUtils.check(GPhoto2Native.INSTANCE.gp_camera_capture_preview(camera, cfile.cf, CameraList.CONTEXT), "gp_camera_capture_preview"); returnedOk = true; @@ -125,11 +126,11 @@ public CameraWidgets newConfiguration() { * Captures a full-quality image image on the camera. * @return camera file, never null. Must be closed afterwards. */ - public CameraFileMod captureImage() { + public CameraFile captureImage() { checkNotClosed(); final CameraFilePath path = new CameraFilePath.ByReference(); CameraUtils.check(GPhoto2Native.INSTANCE.gp_camera_capture(camera, GPhoto2Native.GP_CAPTURE_IMAGE, path, CameraList.CONTEXT), "gp_camera_capture"); - final PathMod p = new PathMod(path); + final Path p = new Path(path); return p.newFile(camera); } diff --git a/src/main/scala/org/gphoto2/CameraFileMod.java b/src/main/scala/org/gphoto2/CameraFile.java similarity index 89% rename from src/main/scala/org/gphoto2/CameraFileMod.java rename to src/main/scala/org/gphoto2/CameraFile.java index 717c3bd..5fdf34d 100644 --- a/src/main/scala/org/gphoto2/CameraFileMod.java +++ b/src/main/scala/org/gphoto2/CameraFile.java @@ -4,22 +4,24 @@ import com.sun.jna.ptr.PointerByReference; import org.gphoto2.jna.GPhoto2Native; -public class CameraFileMod extends CameraFile { +import java.io.Closeable; + +public class CameraFile implements Closeable { final Pointer cf; - public PathMod p; + public Path p; /** * Creates a new file link. The file is not yet linked to any particular camera file - the link is performed later on, by invoking gphoto functions. */ - CameraFileMod() { + CameraFile() { final PointerByReference p = new PointerByReference(); CameraUtils.check(GPhoto2Native.INSTANCE.gp_file_new(p), "gp_file_new"); cf = p.getValue(); } - CameraFileMod(PathMod path) { + CameraFile(Path path) { this(); this.p = path; } @@ -52,7 +54,7 @@ void unref() { CameraUtils.check(GPhoto2Native.INSTANCE.gp_file_unref(cf), "gp_file_unref"); } - public PathMod getPath() { + public Path getPath() { return this.p; } diff --git a/src/main/scala/org/gphoto2/CameraList.java b/src/main/scala/org/gphoto2/CameraList.java new file mode 100644 index 0000000..e891440 --- /dev/null +++ b/src/main/scala/org/gphoto2/CameraList.java @@ -0,0 +1,145 @@ +/** + * Java bindings for the libgphoto2 library. + * Copyright (C) 2011 Innovatrics s.r.o. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.gphoto2; + +import com.sun.jna.Pointer; +import com.sun.jna.ptr.PointerByReference; +import java.io.Closeable; +import java.util.regex.Pattern; +import org.gphoto2.jna.GPhoto2Native; + +/** + * Lists connected cameras. + * @author Martin Vysny + */ +public class CameraList implements Closeable { + + public static final Pointer CONTEXT; + + static { + CONTEXT = GPhoto2Native.INSTANCE.gp_context_new(); + if (CONTEXT == null) { + throw new RuntimeException("Failed to get context"); + } + } + private final Pointer list; + + /** + * Enumerates connected cameras. + */ + public CameraList() { + list = newList(); + populateList(); + } + + private static Pointer newList() { + final PointerByReference ref = new PointerByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_list_new(ref), "gp_list_new"); + return ref.getValue(); + } + private static final Pattern USB_MATCH = Pattern.compile("usb:\\d+,\\d+"); + + private void populateList() { + final Pointer tempList = newList(); + try { + final PointerByReference ref = new PointerByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_abilities_list_new(ref), "gp_abilities_list_new"); + final Pointer cameraAbilitiesList = ref.getValue(); + try { + final PointerByReference ref2 = new PointerByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_port_info_list_new(ref2), "gp_port_info_list_new"); + final Pointer portInfoList = ref2.getValue(); + try { + CameraUtils.check(GPhoto2Native.INSTANCE.gp_port_info_list_load(portInfoList), "gp_port_info_list_load"); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_abilities_list_load(cameraAbilitiesList, CONTEXT), "gp_abilities_list_load"); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_abilities_list_detect(cameraAbilitiesList, portInfoList, tempList, CONTEXT), "gp_abilities_list_detect"); + final int count = CameraUtils.check(GPhoto2Native.INSTANCE.gp_list_count(tempList), "gp_list_count"); + for (int i = 0; i < count; i++) { + final PointerByReference pmodel = new PointerByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_list_get_name(tempList, i, pmodel), "gp_list_get_name"); + final String model = pmodel.getValue().getString(0); + final PointerByReference pvalue = new PointerByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_list_get_value(tempList, i, pvalue), "gp_list_get_value"); + final String path = pvalue.getValue().getString(0); + if (USB_MATCH.matcher(path).matches()) { + CameraUtils.check(GPhoto2Native.INSTANCE.gp_list_append(list, model, path), "gp_list_append"); + } + } + } finally { + GPhoto2Native.INSTANCE.gp_port_info_list_free(portInfoList); + } + } finally { + GPhoto2Native.INSTANCE.gp_abilities_list_free(cameraAbilitiesList); + } + } finally { + GPhoto2Native.INSTANCE.gp_list_free(tempList); + } + } + + /** + * Returns a displayable name of the camera. + * @param i the camera index, must be 0 .. {@link #getCount()} - 1. + * @return the displayable camera name, never null, for example Canon EOS 1000D + */ + public String getModel(int i) { + final PointerByReference pmodel = new PointerByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_list_get_name(list, i, pmodel), "gp_list_get_name"); + return pmodel.getValue().getString(0); + } + + /** + * Returns a displayable name of the port to which the camera is connected. + * @param i the camera index, must be 0 .. {@link #getCount()} - 1. + * @return the displayable camera name, never null, for example usb:002,019 + */ + public String getPort(int i) { + final PointerByReference pvalue = new PointerByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_list_get_value(list, i, pvalue), "gp_list_get_value"); + return pvalue.getValue().getString(0); + } + + /** + * Returns connected camera count. + * @return connected camera count. + */ + public int getCount() { + return CameraUtils.check(GPhoto2Native.INSTANCE.gp_list_count(list), "gp_list_count"); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("CameraList["); + for (int i = 0; i < getCount(); i++) { + sb.append(getModel(i)).append(':').append(getPort(i)).append(", "); + } + sb.append("]"); + return sb.toString(); + } + + public void close() { + CameraUtils.check(GPhoto2Native.INSTANCE.gp_list_free(list), "gp_list_free"); + } + + public Pointer getPortInfo(int index) { + final PointerByReference result = new PointerByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_port_info_list_get_info(list, index, result), "gp_port_info_list_get_info"); + return result.getValue(); + } +} \ No newline at end of file diff --git a/src/main/scala/org/gphoto2/CameraUtils.java b/src/main/scala/org/gphoto2/CameraUtils.java new file mode 100644 index 0000000..7c50af8 --- /dev/null +++ b/src/main/scala/org/gphoto2/CameraUtils.java @@ -0,0 +1,139 @@ +/** + * Java bindings for the libgphoto2 library. Copyright (C) 2011 Innovatrics + * s.r.o. + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.gphoto2; + +import org.gphoto2.jna.GPhoto2Native; + +import java.io.Closeable; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * @author Martin Vysny + */ +public class CameraUtils { + + private CameraUtils() { + throw new AssertionError(); + } + + public static void closeQuietly(Closeable c) { + try { + c.close(); + } catch (Throwable t) { + log.log(Level.WARNING, "Failed to close Closeable " + c.getClass().getName(), t); + } + } + private final static Logger log = Logger.getLogger(CameraUtils.class.getName()); + + public static int check(int result, String methodName) { + if (result < GPhoto2Native.GP_OK) { + String constantName = ERROR_CONSTANTS.get(result); + if (constantName == null) { + constantName = "unknown error"; + } + throw new GPhotoException(methodName + " failed with " + constantName + " #" + result + ": " + GPhoto2Native.INSTANCE.gp_result_as_string(result), result); + } + return result; + } + + public static void checkQuietly(int result, String methodName) { + try { + check(result, methodName); + } catch (Exception ex) { + log.log(Level.WARNING, "Failed to invoke " + methodName + ": " + ex, ex); + } + } + + public static String toString(char[] array) { + for (int i = 0; i < array.length; i++) { + if (array[i] == 0) { + return new String(array, 0, i); + } + } + return new String(array); + } + + public static String toString(byte[] array) { + try { + for (int i = 0; i < array.length; i++) { + if (array[i] == 0) { + return new String(array, 0, i, "ASCII"); + } + } + return new String(array, "ASCII"); + } catch (UnsupportedEncodingException ex) { + throw new RuntimeException(ex); + } + } + private static final Map ERROR_CONSTANTS = new HashMap(); + + static { + final Map m = ERROR_CONSTANTS; + m.put(GPhoto2Native.GP_ERROR_CORRUPTED_DATA, "GP_ERROR_CORRUPTED_DATA"); + m.put(GPhoto2Native.GP_ERROR_FILE_EXISTS, "GP_ERROR_FILE_EXISTS"); + m.put(GPhoto2Native.GP_ERROR_MODEL_NOT_FOUND, "GP_ERROR_MODEL_NOT_FOUND"); + m.put(GPhoto2Native.GP_ERROR_DIRECTORY_NOT_FOUND, "GP_ERROR_DIRECTORY_NOT_FOUND"); + m.put(GPhoto2Native.GP_ERROR_FILE_NOT_FOUND, "GP_ERROR_FILE_NOT_FOUND"); + m.put(GPhoto2Native.GP_ERROR_DIRECTORY_EXISTS, "GP_ERROR_DIRECTORY_EXISTS"); + m.put(GPhoto2Native.GP_ERROR_CAMERA_BUSY, "GP_ERROR_CAMERA_BUSY"); + m.put(GPhoto2Native.GP_ERROR_PATH_NOT_ABSOLUTE, "GP_ERROR_PATH_NOT_ABSOLUTE"); + m.put(GPhoto2Native.GP_ERROR_CANCEL, "GP_ERROR_CANCEL"); + m.put(GPhoto2Native.GP_ERROR_CAMERA_ERROR, "GP_ERROR_CAMERA_ERROR"); + m.put(GPhoto2Native.GP_ERROR_OS_FAILURE, "GP_ERROR_OS_FAILURE"); + m.put(GPhoto2Native.GP_OK, "GP_OK"); + m.put(GPhoto2Native.GP_ERROR, "GP_ERROR"); + m.put(GPhoto2Native.GP_ERROR_BAD_PARAMETERS, "GP_ERROR_BAD_PARAMETERS"); + m.put(GPhoto2Native.GP_ERROR_NO_MEMORY, "GP_ERROR_NO_MEMORY"); + m.put(GPhoto2Native.GP_ERROR_LIBRARY, "GP_ERROR_LIBRARY"); + m.put(GPhoto2Native.GP_ERROR_UNKNOWN_PORT, "GP_ERROR_UNKNOWN_PORT"); + m.put(GPhoto2Native.GP_ERROR_NOT_SUPPORTED, "GP_ERROR_NOT_SUPPORTED"); + m.put(GPhoto2Native.GP_ERROR_IO, "GP_ERROR_IO"); + m.put(GPhoto2Native.GP_ERROR_FIXED_LIMIT_EXCEEDED, "GP_ERROR_FIXED_LIMIT_EXCEEDED"); + m.put(GPhoto2Native.GP_ERROR_TIMEOUT, "GP_ERROR_TIMEOUT"); + m.put(GPhoto2Native.GP_ERROR_IO_SUPPORTED_SERIAL, "GP_ERROR_IO_SUPPORTED_SERIAL"); + m.put(GPhoto2Native.GP_ERROR_IO_SUPPORTED_USB, "GP_ERROR_IO_SUPPORTED_USB"); + m.put(GPhoto2Native.GP_ERROR_IO_INIT, "GP_ERROR_IO_INIT"); + m.put(GPhoto2Native.GP_ERROR_IO_READ, "GP_ERROR_IO_READ"); + m.put(GPhoto2Native.GP_ERROR_IO_WRITE, "GP_ERROR_IO_WRITE"); + m.put(GPhoto2Native.GP_ERROR_IO_UPDATE, "GP_ERROR_IO_UPDATE"); + m.put(GPhoto2Native.GP_ERROR_IO_SERIAL_SPEED, "GP_ERROR_IO_SERIAL_SPEED"); + m.put(GPhoto2Native.GP_ERROR_IO_USB_CLEAR_HALT, "GP_ERROR_IO_USB_CLEAR_HALT"); + m.put(GPhoto2Native.GP_ERROR_IO_USB_FIND, "GP_ERROR_IO_USB_FIND"); + m.put(GPhoto2Native.GP_ERROR_IO_USB_CLAIM, "GP_ERROR_IO_USB_CLAIM"); + m.put(GPhoto2Native.GP_ERROR_IO_LOCK, "GP_ERROR_IO_LOCK"); + m.put(GPhoto2Native.GP_ERROR_HAL, "GP_ERROR_HAL"); + } + + public static T requireNotNull(T obj) { + if (obj == null) { + throw new NullPointerException(); + } + return obj; + } + public static T requireNotNull(T obj, String name) { + if (obj == null) { + throw new NullPointerException(name); + } + return obj; + } +} \ No newline at end of file diff --git a/src/main/scala/org/gphoto2/CameraWidgets.java b/src/main/scala/org/gphoto2/CameraWidgets.java new file mode 100644 index 0000000..2dcad20 --- /dev/null +++ b/src/main/scala/org/gphoto2/CameraWidgets.java @@ -0,0 +1,472 @@ +/** + * Java bindings for the libgphoto2 library. + * Copyright (C) 2011 Innovatrics s.r.o. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.gphoto2; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.ptr.FloatByReference; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.PointerByReference; +import org.gphoto2.jna.GPhoto2Native; + +import java.io.Closeable; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.util.*; + +/** + * Represents a list of configuration items (called widgets by gphoto). + * @author Martin Vysny + */ +public final class CameraWidgets implements Closeable { + + public void close() { + if (rootWidget != null) { + CameraUtils.checkQuietly(GPhoto2Native.INSTANCE.gp_widget_free(rootWidget), "gp_widget_free"); + rootWidget = null; + } + widgets.clear(); + } + + private void checkNotClosed() { + if (rootWidget == null) { + throw new IllegalStateException("Invalid state: closed"); + } + if (camera.isClosed()) { + throw new IllegalStateException("Invalid state: camera is closed"); + } + } + + public static enum WidgetTypeEnum { + + /** + * Window widget This is the toplevel configuration widget. It should likely contain multiple GP_WIDGET_SECTION entries. + */ + Window(GPhoto2Native.GP_WIDGET_WINDOW, false, false, false, null), + /** + * Section widget (think Tab). + */ + Section(GPhoto2Native.GP_WIDGET_SECTION, false, false, false, null), + /** + * Text widget. + */ + Text(GPhoto2Native.GP_WIDGET_TEXT, true, false, true, String.class), + /** + * Slider widget. + */ + Range(GPhoto2Native.GP_WIDGET_RANGE, true, false, false, Float.class), + /** + * Toggle widget (think check box). + */ + Toggle(GPhoto2Native.GP_WIDGET_TOGGLE, true, false, true, Boolean.class), + /** + * Radio button widget. + */ + Radio(GPhoto2Native.GP_WIDGET_RADIO, true, true, true, String.class), + /** + * Menu widget (same as {@link #Radio}). + */ + Menu(GPhoto2Native.GP_WIDGET_MENU, true, true, true, String.class), + /** + * Button press widget. + */ + Button(GPhoto2Native.GP_WIDGET_BUTTON, true, false, true, Void.class), + /** + * Date entering widget. + */ + Date(GPhoto2Native.GP_WIDGET_DATE, true, false, false, Date.class); + public final int cval; + public final boolean hasValue; + public final boolean hasChoices; + public final boolean acceptNullValue; + public final Class valueType; + + private WidgetTypeEnum(int cval, boolean hasValue, boolean hasChoices, boolean acceptNullValue, Class valueType) { + this.cval = cval; + this.hasValue = hasValue; + this.hasChoices = hasChoices; + this.valueType = valueType; + this.acceptNullValue = acceptNullValue; + } + + public static WidgetTypeEnum fromCVal(int cval) { + for (WidgetTypeEnum e : WidgetTypeEnum.values()) { + if (e.cval == cval) { + return e; + } + } + throw new IllegalArgumentException("Parameter cval: invalid value " + cval + ": no such widget type"); + } + + public boolean acceptsValue(Object value) { + if (valueType == null) { + return false; + } + if (value == null) { + return acceptNullValue; + } + return valueType.isInstance(value); + } + } + private Map widgets = new HashMap(); + private final Camera camera; + private Pointer rootWidget; + + /** + * Lists all configuration options for given camera. + */ + CameraWidgets(Camera c) { + camera = c; + final PointerByReference ptrRoot = new PointerByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_widget_new(WidgetTypeEnum.Window.cval, "", ptrRoot), "gp_widget_new"); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_camera_get_config(c.camera, ptrRoot, CameraList.CONTEXT), "gp_camera_get_config"); + rootWidget = ptrRoot.getValue(); + try { + enumWidgets(rootWidget, ""); + } catch (RuntimeException ex) { + close(); + throw ex; + } + } + + private void enumWidgets(Pointer widget, String name) { + checkNotClosed(); + final IntByReference type = new IntByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_widget_get_type(widget, type), "gp_widget_get_type"); + final WidgetTypeEnum t = WidgetTypeEnum.fromCVal(type.getValue()); + if (t.hasValue) { + widgets.put(name, widget); + } + final int childcount = CameraUtils.check(GPhoto2Native.INSTANCE.gp_widget_count_children(widget), "gp_widget_count_children"); + for (int i = 0; i < childcount; i++) { + final PointerByReference ptrWidget = new PointerByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_widget_get_child(widget, i, ptrWidget), "gp_widget_get_child"); + enumWidgets(ptrWidget.getValue(), name + "/" + getBasename(ptrWidget.getValue())); + } + } + + /** + * Return names of all widgets available. + * @return a list of widgets, never null, may be empty. + */ + public List getNames() { + checkNotClosed(); + final List result = new ArrayList(widgets.keySet()); + Collections.sort(result); + return result; + } + + private String getBasename(Pointer widget) { + checkNotClosed(); + final PointerByReference pref = new PointerByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_widget_get_name(widget, pref), "gp_widget_get_name"); + final Pointer p = pref.getValue(); + return p.getString(0); + } + + /** + * Returns the label of the widget. + * @param name the widget name + * @return widget label. + */ + public String getLabel(String name) { + checkNotClosed(); + final PointerByReference pref = new PointerByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_widget_get_label(get(name), pref), "gp_widget_get_label"); + final Pointer p = pref.getValue(); + return p.getString(0); + } + + /** + * Returns the info for the widget. + * @param name the widget name + * @return widget info. + */ + public String getInfo(String name) { + checkNotClosed(); + final PointerByReference pref = new PointerByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_widget_get_info(get(name), pref), "gp_widget_get_info"); + final Pointer p = pref.getValue(); + return p.getString(0); + } + + /** + * Returns the data type of the widget. + * @param name the widget name + * @return widget type, never null. + */ + public WidgetTypeEnum getType(String name) { + checkNotClosed(); + final IntByReference type = new IntByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_widget_get_type(get(name), type), "gp_widget_get_type"); + return WidgetTypeEnum.fromCVal(type.getValue()); + } + + /** + * Returns the value of the configuration option. + * @param name the widget name + * @return the value. + */ + public Object getValue(String name) { + checkNotClosed(); + final WidgetTypeEnum type = getType(name); + switch (type) { + case Text: + case Radio: + case Menu: { + final PointerByReference pref = new PointerByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_widget_get_value(get(name), pref), "gp_widget_get_value"); + final Pointer p = pref.getValue(); + return p == null ? null : p.getString(0); + } + case Range: { + final FloatByReference pref = new FloatByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_widget_get_value(get(name), pref), "gp_widget_get_value"); + return pref.getValue(); + } + case Toggle: { + final IntByReference pref = new IntByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_widget_get_value(get(name), pref), "gp_widget_get_value"); + return pref.getValue() == 2 ? null : pref.getValue() == 1; + } + case Date: { + final IntByReference pref = new IntByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_widget_get_value(get(name), pref), "gp_widget_get_value"); + return new Date(((long) pref.getValue()) * 1000L); + } + case Button: + return null; + default: + throw new IllegalArgumentException("Parameter name: invalid value " + name + ": unsupported type: " + type); + } + } + + /** + * Sets the value of given property. The value must be of correct class. + *

+ * Important: after the changes are made, the {@link #apply()} method must be called, to apply the new values. + * @param name the property name, not null + * @param value the value, may be null. + */ + public void setValue(String name, Object value) { + checkNotClosed(); + if (isReadOnly(name)) { + throw new IllegalArgumentException("Parameter name: invalid value " + name + ": read-only"); + } + final WidgetTypeEnum type = getType(name); + if (!type.acceptsValue(value)) { + throw new IllegalArgumentException("Parameter value: invalid value " + value + ": expected " + type.valueType + " but got " + (value == null ? "null" : value.getClass())); + } + final Pointer ptr; + switch (type) { + case Text: + case Radio: + case Menu: { + if (value == null) { + ptr = null; + } else { + final byte[] b; + try { + b = ((String) value).getBytes("ASCII"); + } catch (UnsupportedEncodingException ex) { + throw new RuntimeException(ex); + } + // patched as shown in https://code.google.com/p/gphoto2-java/issues/detail?id=5 + final ByteBuffer buf = ByteBuffer.allocateDirect(b.length + 1); + buf.put(b); + ptr = Native.getDirectBufferPointer(buf); + } + } + break; + case Range: + ptr = new FloatByReference((Float) value).getPointer(); + break; + case Toggle: { + final int val = value == null ? 2 : (Boolean) value ? 1 : 0; + ptr = new IntByReference(val).getPointer(); + } + break; + case Date: + ptr = new IntByReference((int) (((Date) value).getTime() / 1000)).getPointer(); + break; + case Button: + setChanged(name, true); + return; + default: + throw new IllegalArgumentException("Parameter type: invalid value " + type + ": unsupported"); + } + CameraUtils.check(GPhoto2Native.INSTANCE.gp_widget_set_value(get(name), ptr), "gp_widget_set_value"); + } + + private void checkType(String name, WidgetTypeEnum... types) { + final WidgetTypeEnum type = getType(name); + if (!Arrays.asList(types).contains(type)) { + throw new IllegalArgumentException("Parameter name: invalid value " + name + ": expected " + Arrays.toString(types) + " but got " + type); + } + } + + /** + * Returns allowed range for {@link WidgetTypeEnum#Range} options. + * @param name the widget name. + * @return the range. + */ + public Range getRange(String name) { + checkType(name, WidgetTypeEnum.Range); + return new Range(get(name)); + } + + public void setChanged(String name, boolean changed) { + checkNotClosed(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_widget_set_changed(get(name), changed ? 1 : 0), "gp_widget_set_changed"); + } + + public boolean isChanged(String name) { + return CameraUtils.check(GPhoto2Native.INSTANCE.gp_widget_changed(get(name)), "gp_widget_changed") == 1; + } + + private Pointer get(String name) { + CameraUtils.requireNotNull(name, "name"); + checkNotClosed(); + final Pointer ptr = widgets.get(name); + if (ptr == null) { + throw new IllegalArgumentException("Parameter name: invalid value " + name + ": the name is not known"); + } + return ptr; + } + + /** + * Lists choices for given widget. Only applicable to {@link WidgetTypeEnum#Radio} and {@link WidgetTypeEnum#Menu} types. + * @param name widget name. + * @return list of possible choices captions. + */ + public List listChoices(String name) { + final WidgetTypeEnum type = getType(name); + if (!type.hasChoices) { + throw new IllegalArgumentException("Parameter name: invalid value " + name + ": is of type " + type + " which does not have any choices."); + } + final Pointer widget = get(name); + final int choiceCount = CameraUtils.check(GPhoto2Native.INSTANCE.gp_widget_count_choices(widget), "gp_widget_count_choices"); + final List result = new ArrayList(choiceCount); + for (int i = 0; i < choiceCount; i++) { + final PointerByReference pref = new PointerByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_widget_get_choice(widget, i, pref), "gp_widget_get_choice"); + final Pointer p = pref.getValue(); + result.add(p.getString(0)); + } + return result; + } + + public boolean isReadOnly(String name) { + checkNotClosed(); + final IntByReference result = new IntByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_widget_get_readonly(get(name), result), "gp_widget_get_readonly"); + return result.getValue() == 1; + } + + @Override + public String toString() { + return "Widgets: " + getNames(); + } + + /** + * Returns a debug description of all options, their types, descriptions, allowed values etc. + * @return a formatted string of all options. + */ + public String inspect() { + checkNotClosed(); + final StringBuilder sb = new StringBuilder(); + for (final String name : getNames()) { + final WidgetTypeEnum type = getType(name); + sb.append(name).append(": ").append(type).append(" = "); + final Object value = getValue(name); + sb.append(type.valueType.getName()).append(": ").append(value); + sb.append('\n'); + sb.append(" ").append(getLabel(name)); + final String info = getInfo(name); + if (info != null && !info.trim().isEmpty()) { + sb.append(" - ").append(info); + } + if (type.hasChoices) { + sb.append(": ").append(listChoices(name)); + } + if (type == WidgetTypeEnum.Range) { + sb.append(": ").append(getRange(name)); + } + if (isReadOnly(name)) { + sb.append(": READ_ONLY"); + } + sb.append('\n'); + } + return sb.toString(); + } + + /** + * If the settings are altered, they need to be applied to take effect. + */ + public void apply() { + checkNotClosed(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_camera_set_config(camera.camera, rootWidget, CameraList.CONTEXT), "gp_camera_set_config"); + } + + public static void main(String[] args) { + final Camera c = new Camera(); + c.initialize(); + try { + final CameraWidgets w = c.newConfiguration(); + System.out.println(w.inspect()); + w.close(); + } finally { + CameraUtils.closeQuietly(c); + } + } + + /** + * Represents a {@link WidgetTypeEnum#Range}. + */ + public static class Range { + + /** + * The minimum accepted value. + */ + public final float min; + /** + * The maximum accepted value. + */ + public final float max; + /** + * The stepping. + */ + public final float step; + + Range(Pointer widget) { + final FloatByReference min = new FloatByReference(); + final FloatByReference max = new FloatByReference(); + final FloatByReference step = new FloatByReference(); + CameraUtils.check(GPhoto2Native.INSTANCE.gp_widget_get_range(widget, min, max, step), "gp_widget_get_range"); + this.min = min.getValue(); + this.max = max.getValue(); + this.step = step.getValue(); + } + + @Override + public String toString() { + return "Range{" + min + ".." + max + ", step=" + step + '}'; + } + } +} \ No newline at end of file diff --git a/src/main/scala/org/gphoto2/GPhotoException.java b/src/main/scala/org/gphoto2/GPhotoException.java new file mode 100644 index 0000000..5cf8236 --- /dev/null +++ b/src/main/scala/org/gphoto2/GPhotoException.java @@ -0,0 +1,39 @@ +/** + * Java bindings for the libgphoto2 library. + * Copyright (C) 2011 Innovatrics s.r.o. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.gphoto2; + +/** + * Thrown by the GPhoto Java bindings. + * @author Martin Vysny + */ +public class GPhotoException extends RuntimeException { + + private static final long serialVersionUID = 1L; + public final int result; + + public GPhotoException(String message, Throwable cause, int result) { + super(message, cause); + this.result = result; + } + + public GPhotoException(String message, int result) { + super(message); + this.result = result; + } +} \ No newline at end of file diff --git a/src/main/scala/org/gphoto2/PathMod.java b/src/main/scala/org/gphoto2/Path.java similarity index 88% rename from src/main/scala/org/gphoto2/PathMod.java rename to src/main/scala/org/gphoto2/Path.java index eb74b62..5ac1502 100644 --- a/src/main/scala/org/gphoto2/PathMod.java +++ b/src/main/scala/org/gphoto2/Path.java @@ -9,7 +9,7 @@ /** * Represents a path of a camera file. */ -public class PathMod { +public class Path { public final String filename; public final String path; @@ -20,12 +20,12 @@ public class PathMod { * @param filename the file name, without the path, gphoto-dependent. See {@link CameraFilePath} for details. * @param path the path, gphoto-dependent. */ - public PathMod(String filename, String path) { + public Path(String filename, String path) { this.filename = filename; this.path = path; } - public PathMod(CameraFilePath path) { + public Path(CameraFilePath path) { this.filename = CameraUtils.toString(path.name); this.path = CameraUtils.toString(path.folder); } @@ -45,9 +45,9 @@ public String toString() { * @param cam the camera handle. * @return camera file. */ - CameraFileMod newFile(Pointer cam) { + CameraFile newFile(Pointer cam) { boolean returnedOk = false; - final CameraFileMod cf = new CameraFileMod(this); + final CameraFile cf = new CameraFile(this); try { // use instead of filename when https://github.com/gphoto/gphoto2/issues/48 is solved String jpgName = GFile.rawFileNameToJpg(this.filename); // Use it later when gphoto2 can read images when photo was taken with RAW+JPG mode diff --git a/src/main/scala/org/gphoto2/jna/GPhoto2Native.java b/src/main/scala/org/gphoto2/jna/GPhoto2Native.java new file mode 100644 index 0000000..6ee74b0 --- /dev/null +++ b/src/main/scala/org/gphoto2/jna/GPhoto2Native.java @@ -0,0 +1,412 @@ +/** + * Java bindings for the libgphoto2 library. Copyright (C) 2011 Innovatrics + * s.r.o. + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.gphoto2.jna; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.ptr.ByReference; +import com.sun.jna.ptr.FloatByReference; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.PointerByReference; + +import java.util.Arrays; +import java.util.List; + +/** + * Native binding for GPhoto2. + * + * @author Martin Vysny + */ +public interface GPhoto2Native extends Library { + + /** + * Corrupted data received + * + * Data is corrupt. This error is reported by camera drivers if corrupted + * data has been received that can not be automatically handled. Normally, + * drivers will do everything possible to automatically recover from this + * error. + * + */ + int GP_ERROR_CORRUPTED_DATA = -102; + /** + * File already exists + * + * An operation failed because a file existed. This error is reported for + * example when the user tries to create a file that already exists. + * + */ + int GP_ERROR_FILE_EXISTS = -103; + /** + * Specified camera model was not found + * + * The specified model could not be found. This error is reported when the + * user specified a model that does not seem to be supported by any driver. + * + */ + int GP_ERROR_MODEL_NOT_FOUND = -105; + /** + * Specified directory was not found + * + * The specified directory could not be found. This error is reported when + * the user specified a directory that is non-existent. + * + */ + int GP_ERROR_DIRECTORY_NOT_FOUND = -107; + /** + * Specified file was not found + * + * The specified file could not be found. This error is reported when the + * user wants to access a file that is non-existent. + * + */ + int GP_ERROR_FILE_NOT_FOUND = -108; + /** + * Specified directory already exists + * + * The specified directory already exists. This error is reported for + * example when the user wants to create a directory that already exists. + * + */ + int GP_ERROR_DIRECTORY_EXISTS = -109; + /** + * The camera is already busy + * + * Camera I/O or a command is in progress. + * + */ + int GP_ERROR_CAMERA_BUSY = -110; + /** + * Path is not absolute + * + * The specified path is not absolute. This error is reported when the user + * specifies paths that are not absolute, i.e. paths like + * "path/to/directory". As a rule of thumb, in gphoto2, there is nothing + * like relative paths. + * + */ + int GP_ERROR_PATH_NOT_ABSOLUTE = -111; + /** + * Cancellation successful. + * + * A cancellation requestion by the frontend via progress callback and + * GP_CONTEXT_FEEDBACK_CANCEL was successful and the transfer has been + * aborted. + */ + int GP_ERROR_CANCEL = -112; + /** + * Unspecified camera error + * + * The camera reported some kind of error. This can be either a photographic + * error, such as failure to autofocus, underexposure, or violating storage + * permission, anything else that stops the camera from performing the + * operation. + */ + int GP_ERROR_CAMERA_ERROR = -113; + /** + * Unspecified failure of the operating system + * + * There was some sort of OS error in communicating with the camera, e.g. + * lack of permission for an operation. + */ + int GP_ERROR_OS_FAILURE = -114; + /** + * \brief Everything is OK + * + * Note that this is also the value 0, and every error is negative (lower). + */ + int GP_OK = 0; + /** + * \brief Generic Error + */ + int GP_ERROR = -1; + /** + * \brief Bad parameters passed + */ + int GP_ERROR_BAD_PARAMETERS = -2; + /** + * \brief Out of memory + */ + int GP_ERROR_NO_MEMORY = -3; + /** + * \brief Error in the camera driver + */ + int GP_ERROR_LIBRARY = -4; + /** + * \brief Unknown libgphoto2 port passed + */ + int GP_ERROR_UNKNOWN_PORT = -5; + /** + * \brief Functionality not supported + */ + int GP_ERROR_NOT_SUPPORTED = -6; + /** + * \brief Generic I/O error + */ + int GP_ERROR_IO = -7; + /** + * \brief Buffer overflow of internal structure + */ + int GP_ERROR_FIXED_LIMIT_EXCEEDED = -8; + /** + * \brief Operation timed out + */ + int GP_ERROR_TIMEOUT = -10; + /** + * \brief Serial ports not supported + */ + int GP_ERROR_IO_SUPPORTED_SERIAL = -20; + /** + * \brief USB ports not supported + */ + int GP_ERROR_IO_SUPPORTED_USB = -21; + /** + * \brief Error initialising I/O + */ + int GP_ERROR_IO_INIT = -31; + /** + * \brief I/O during read + */ + int GP_ERROR_IO_READ = -34; + /** + * \brief I/O during write + */ + int GP_ERROR_IO_WRITE = -35; + /** + * \brief I/O during update of settings + */ + int GP_ERROR_IO_UPDATE = -37; + /** + * \brief Specified serial speed not possible. + */ + int GP_ERROR_IO_SERIAL_SPEED = -41; + /** + * \brief Error during USB Clear HALT + */ + int GP_ERROR_IO_USB_CLEAR_HALT = -51; + /** + * \brief Error when trying to find USB device + */ + int GP_ERROR_IO_USB_FIND = -52; + /** + * \brief Error when trying to claim the USB device + */ + int GP_ERROR_IO_USB_CLAIM = -53; + /** + * \brief Error when trying to lock the device + */ + int GP_ERROR_IO_LOCK = -60; + /** + * \brief Unspecified error when talking to HAL + */ + int GP_ERROR_HAL = -70; + int GP_CAPTURE_IMAGE = 0; + int GP_CAPTURE_MOVIE = 1; + int GP_CAPTURE_SOUND = 2; + int GP_FILE_TYPE_NORMAL = 1; + int GP_VERSION_SHORT = 0; + int GP_VERSION_VERBOSE = 1; + GPhoto2Native INSTANCE = (GPhoto2Native) Native.loadLibrary("gphoto2", GPhoto2Native.class); + + int gp_camera_new(PointerByReference pcamera); + + int gp_camera_init(Pointer pcamera, Pointer gpcontext); + + int gp_camera_exit(Pointer pcamera, Pointer gpcontext); + + int gp_camera_free(Pointer pcamera); + + Pointer gp_context_new(); + + String gp_result_as_string(int result); + + int gp_file_new(PointerByReference p); + + int gp_file_free(Pointer cf); + + int gp_camera_capture_preview(Pointer camera, Pointer cf, Pointer context); + + int gp_file_save(Pointer cf, String filename); + + int gp_camera_capture(Pointer camera, int GP_CAPTURE_IMAGE, CameraFilePath path, Pointer context); + + int gp_camera_file_get(Pointer cam, String path, String filename, int GP_FILE_TYPE_NORMAL, Pointer cf, Pointer context); + + int gp_camera_ref(Pointer camera); + + int gp_camera_unref(Pointer camera); + + int gp_camera_get_config(Pointer camera, PointerByReference widget, Pointer context); + + int gp_camera_set_config(Pointer camera, Pointer widget, Pointer context); + + int gp_file_ref(Pointer cf); + + int gp_file_unref(Pointer cf); + + String[] gp_library_version(int GP_VERSION_VERBOSE); + + int gp_list_new(PointerByReference ref); + + int gp_list_free(Pointer list); + + int gp_port_info_list_new(PointerByReference ref); + + int gp_port_info_list_load(Pointer list); + + int gp_port_info_list_count(Pointer list); + + int gp_abilities_list_new(PointerByReference ref); + + int gp_abilities_list_load(Pointer ptr, Pointer ctx); + + int gp_abilities_list_detect(Pointer cameraAbilitiesList, Pointer portInfoList, Pointer list, Pointer context); + + int gp_list_count(Pointer list); + + int gp_list_get_name(Pointer list, int i, PointerByReference pmodel); + + int gp_list_append(Pointer list, String model, String path); + + int gp_list_get_value(Pointer tempList, int i, PointerByReference pvalue); + + void gp_abilities_list_free(Pointer cameraAbilitiesList); + + void gp_port_info_list_free(Pointer portInfoList); + + int gp_port_info_list_get_info(Pointer portInfoList, int n, PointerByReference portInfo); + + int gp_camera_set_port_info(Pointer camera, Pointer portInfo); + + int gp_file_clean(Pointer cf); + + /** + * A structure created by the capture operation. + * + * A structure containing the folder and filename of an object after a + * successful capture and is passed as reference to the gp_camera_capture() + * function. + */ + class CameraFilePath extends Structure { + { + // must not call with JNA 3.5.0 or higher. +// setFieldOrder(new String[] { "name", "folder" }); + } + /** + * Name of the captured file. + */ + public byte[] name = new byte[128]; + /** + * Name of the folder of the captured file. + */ + public byte[] folder = new byte[1024]; + + public List getFieldOrder() { + // fixes compatibility with JNA 3.5.0 and higher. + // see https://github.com/mvysny/gphoto2-java/issues/10 for details. + return Arrays.asList("name", "folder"); + } + + public static class ByReference extends CameraFilePath implements Structure.ByReference { + }; + } + int GP_WIDGET_WINDOW = 0;// # Window widget This is the toplevel configuration widget. It should likely contain multiple GP_WIDGET_SECTION entries. + int GP_WIDGET_SECTION = 1;// # Section widget (think Tab). + int GP_WIDGET_TEXT = 2;// # Text widget. + int GP_WIDGET_RANGE = 3;// # Slider widget. + int GP_WIDGET_TOGGLE = 4;// # Toggle widget (think check box). + int GP_WIDGET_RADIO = 5;// # Radio button widget. + int GP_WIDGET_MENU = 6;// # Menu widget (same as RADIO). + int GP_WIDGET_BUTTON = 7;// # Button press widget. + int GP_WIDGET_DATE = 8;// # Date entering widget. + + int gp_widget_new(int type, String label, PointerByReference widget); + + int gp_widget_free(Pointer widget); + + int gp_widget_ref(Pointer widget); + + int gp_widget_unref(Pointer widget); + + int gp_widget_append(Pointer widget, Pointer child); + + int gp_widget_prepend(Pointer widget, Pointer child); + + int gp_widget_count_children(Pointer widget); + + int gp_widget_get_child(Pointer widget, int child_number, PointerByReference child); + + /* Retrieve Widgets */ + int gp_widget_get_child_by_label(Pointer widget, String label, PointerByReference child); + + int gp_widget_get_child_by_id(Pointer widget, int id, PointerByReference child); + + int gp_widget_get_child_by_name(Pointer widget, String name, PointerByReference child); + + int gp_widget_get_root(Pointer widget, PointerByReference root); + + int gp_widget_get_parent(Pointer widget, PointerByReference parent); + + int gp_widget_set_value(Pointer widget, Pointer value); + + int gp_widget_get_value(Pointer widget, ByReference value); + + int gp_widget_set_name(Pointer widget, String name); + + int gp_widget_get_name(Pointer widget, PointerByReference name); + + int gp_widget_set_info(Pointer widget, String info); + + int gp_widget_get_info(Pointer widget, PointerByReference /** + * char ** * + */ + info); + + int gp_widget_get_id(Pointer widget, IntByReference id); + + int gp_widget_get_type(Pointer widget, IntByReference type); + + int gp_widget_get_label(Pointer widget, PointerByReference /** + * char ** * + */ + label); + + int gp_widget_set_range(Pointer range, float low, float high, float increment); + + int gp_widget_get_range(Pointer range, FloatByReference min, FloatByReference max, FloatByReference increment); + + int gp_widget_add_choice(Pointer widget, String choice); + + int gp_widget_count_choices(Pointer widget); + + int gp_widget_get_choice(Pointer widget, int choice_number, PointerByReference /** + * char ** * + */ + choice); + + int gp_widget_changed(Pointer widget); + + int gp_widget_set_changed(Pointer widget, int changed); + + int gp_widget_set_readonly(Pointer widget, int readonly); + + int gp_widget_get_readonly(Pointer widget, IntByReference readonly); +} \ No newline at end of file