android externals

Moderators: FourthWorld, heatherlaine, Klaus, kevinmiller, LCMark

Locked
monte
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 1564
Joined: Fri Jan 13, 2012 1:47 am
Contact:

android externals

Post by monte » Mon Jul 08, 2013 2:51 am

Has anyone played with or looked at the latest android externals stuff in runrevmark's externals api v5 branch? I'm investigating the possibility that it's ready to start working with. First thing that I notice is there seems to be no way to define the package name of your class. Perhaps a use clause so for mergAV:

use java-package com.merg

Which would then modify the JNI_OnLoad to FindClass("com/merg/mergAV")

The next thing is shouldn't the lcidl compiler be pumping out Support.java somewhere?

My other comment is would it be simpler to subclass the LC class in Support.java and use that as the basis for our external? If we did that then couldn't we put the com.runrev.external package next to LiveCode.h and set our classpath appropriately in the android make files? Subclassing would save us having to initialise the LC object too...
LiveCode User Group on Facebook : http://FaceBook.com/groups/LiveCodeUsers/

monte
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 1564
Joined: Fri Jan 13, 2012 1:47 am
Contact:

Re: android externals

Post by monte » Mon Jul 08, 2013 3:39 am

Thinking about things some more and I realised how limited an android externals sdk will be without the ability to handle intent results. So I think we need some way to register a method to handle an intent. One way I'm thinking is if the externals api has a method... startIntent(Intent,LCIntentHandler) so the engine's java side starts the intent with a requestCode it has mapped to the LCIntentHandler object. When onActivityResult is invoked the LCIntentHandler object's intentResult method is invoked with the intent object.

Any thoughts?
LiveCode User Group on Facebook : http://FaceBook.com/groups/LiveCodeUsers/

LCMark
Livecode Staff Member
Livecode Staff Member
Posts: 1206
Joined: Thu Apr 11, 2013 11:27 am

Re: android externals

Post by LCMark » Mon Jul 08, 2013 12:48 pm

I'm investigating the possibility that it's ready to start working with. First thing that I notice is there seems to be no way to define the package name of your class. Perhaps a use clause so for mergAV:
I'm not sure it is quite ready to start working with - I've been trying to find a couple of hours to at least get it to a basic working point and am still hopeful to do so soon :)

The package name is taken from the name of the external - the 'external' clause has been updated to be a qualified id (e.g. com.runrev.mytestexternal).
My other comment is would it be simpler to subclass the LC class in Support.java and use that as the basis for our external? If we did that then couldn't we put the com.runrev.external package next to LiveCode.h and set our classpath appropriately in the android make files? Subclassing would save us having to initialise the LC object too...
The problem here is that of conflicts with names of packages and types etc.. All externals are essentially independent - all the Java related interaction is in the external, rather than in the engine. So, when you build an android external, lcidlc will generate the c++ source and a suitable set of support class files which can then be used to compile the Java part of the external. (The point here is that the package name in Support.java is just a place-holder, rather than what the package will actually be). i.e. Each external needs to be an entirely independent package.

As currently posed, Support.java will actually present an LC class within the namespace of the external (e.g. com.runrev.mytestexternal) so that there are no conflicts with naming across externals compiled potentially with different versions of lcidlc. The other reason not to subclass here is that by keeping things in LC.<etc>, there's no overlap (from usage within the external files) with Java types of similar names. It also makes the naming consistent with native code: e.g. from within the external java files (all in the same package, e.g. com.runrev.mytestexternal) 'new LC.Array()' would create an LC array.

I did consider sub-classing, but then you'd have to subclass all classes that would want access to the LC class (which means you can't subclass anything else). With it being a top-level (public) class within the package you don't need to do anything, all classes have access to everything by just using LC.<whatever> explicitly.

The current idea with the actual external implementation is that you define a class with a standard name (maybe com.runrev.mytestexternal.external) within the package (com.runrev.mytestexternal) and it is that class which contains the external methods (implemented in Java). Like the LC class this will essentially be a shim (from the point of view the engine at least), in that it just contains static methods and variables so no instantiation is required - just the Class pointer on the native code side to search and invoke static methods on. Of course, within this class (and the implementation of the external methods) you will be free to do as you wish - the interface to the engine is through the LC class, and the engine looks up and calls the static methods in your external class for the external handlers that you've declared in lcidlc.

monte
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 1564
Joined: Fri Jan 13, 2012 1:47 am
Contact:

Re: android externals

Post by monte » Mon Jul 08, 2013 1:25 pm

OK, I think I understand how it will all fit together now... any thoughts on my intent idea? Perhaps two protocols... one for LiveCodeActivity to implement so that we can call the method to trigger the intent and one for our external class to implement so we can handle the intent. You have an int sequence for intent requestCodes starting with the highest number you use for the built in intents. It's incremented for each intent triggered by an external and the object implementing the intent result handler protocol is added to a dictionary for lookup in onActivityResult via the requestCode. When found it calls the appropriate method then removes the object from the dictionary.

You could use a subclass between Activity and LiveCodeActivity rather than the protocol there...
LiveCode User Group on Facebook : http://FaceBook.com/groups/LiveCodeUsers/

Janschenkel
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 977
Joined: Sat Apr 08, 2006 7:47 am
Location: Aalst, Belgium
Contact:

Re: android externals

Post by Janschenkel » Mon Jul 08, 2013 4:47 pm

@runrevmark:
Have you had a chance to look at what I did in the qrtjvm external code? It's available on github; all I have left to do at this point is write a Linux make script.
Even though it's nice that the .lcidl mechanism can now generate a wrapper for the java code, I'd rather see something that doesn't require any C++ code at all. Especially as part of a larger strategy for embedding VM's (JavaVM or .NET/Mono CLR on desktop).
Quartam Reports & PDF Library for LiveCode
www.quartam.com

monte
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 1564
Joined: Fri Jan 13, 2012 1:47 am
Contact:

Re: android externals

Post by monte » Tue Jul 09, 2013 12:50 am

@Janschenkel do you see the lcidl + class approach being a blocker for desktop JVM support? I kind of like that all the parameter checking etc is done by the lcidl generated code before the method is called.
LiveCode User Group on Facebook : http://FaceBook.com/groups/LiveCodeUsers/

Janschenkel
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 977
Joined: Sat Apr 08, 2006 7:47 am
Location: Aalst, Belgium
Contact:

Re: android externals

Post by Janschenkel » Tue Jul 09, 2013 4:41 am

@monte
As I've said before, the .lcidl is not a blocker - just an unneeded extra hurdle where Java reflection suffices. We can always write an annotation processor to spit out the .lcidl file if it's really needed, but I don't think we need this layer of indirection when the engine can do it dynamically for us at runtime (including parameter checking). Unless there's a clear performance or portability advantage, generated code is just another place where things can go wrong, and one that's harder to track down at that.
I would much rather have a Unified Externals API that is designed for us to plug other technologies into. And then you can have one implementation with .lcidl files on top of it, and another which uses reflection-like mechanisms, and even a third one which does something else we haven't thought up yet.
Quartam Reports & PDF Library for LiveCode
www.quartam.com

monte
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 1564
Joined: Fri Jan 13, 2012 1:47 am
Contact:

Re: android externals

Post by monte » Tue Jul 09, 2013 6:19 am

OK, I'm open to whatever. I just need it yesterday and need to know which direction we are going so I can help ;-)

I think what you want in the unified externals api is already there in the interface and we could do what we like with it. We aren't required to use the lcidl compiler and all the details of the interface are there in a generated file and the engine.
LiveCode User Group on Facebook : http://FaceBook.com/groups/LiveCodeUsers/

LCMark
Livecode Staff Member
Livecode Staff Member
Posts: 1206
Joined: Thu Apr 11, 2013 11:27 am

Re: android externals

Post by LCMark » Tue Jul 09, 2013 10:50 am

Unless there's a clear performance or portability advantage, generated code is just another place where things can go wrong, and one that's harder to track down at that.
Well, if you are concerned about well-debugged tools breaking then we might as well all go back to writing machine code directly - after all compilers produce 'generated code' ;)
As I've said before, the .lcidl is not a blocker - just an unneeded extra hurdle where Java reflection suffices
This is making the assumption that Java reflection *does* suffice for mapping LiveCode level types to 'native' code (whether that be C++, Obj-C, Java or any other language). Java does provide a significant amount of information through its reflection process, but there are other things that the (replacement for) lcidl spec files will allow you to specify that goes outside the scope of any reflection process because they are related to LiveCode syntax and typing. It might be that an annotation processor could be written - but then isn't that another tool that could go wrong when we will (eventually) have one that performs the function for all language bindings? ;)
I think what you want in the unified externals api is already there in the interface and we could do what we like with it. We aren't required to use the lcidl compiler and all the details of the interface are there in a generated file and the engine.
The only supported mechanism for accessing the externals functionality at the moment is through the v0 interface and through the lcidl mechanism - the v1 interface is too inherently tied to the engine internals and will be gone in the refactored engine (so if you want what you write now to work moving forward, then don't touch the raw v1 interface!)... The access to variables it exposes is too low-level and means it can't be changed to use the new reference counted typing system that is being introduced without a shim that completely copies arrays and the like - this is something we are trying to move away from to ensure that there is no performance penalty anywhere we can avoid it. (By the way, in terms of forward-compatibility for existing iOS externals, lcidlc will just be changed so that it generates the new specification information rather than generating code; and for forward-compatibility for v0 externals, a shim will be written that emulates the v0 interface although that will be immediately deprecated).

An important point to heed is that 'externals' in their current form are going away - *all* engine functionality is being refactored into distinct modules where the functionality is completely separated from all LiveCode-script level concerns. The bindings of these modules to script will be through a specification file. When this work is completed externals as they are now will be no different from any other module - they won't be second-class citizens.

Essentially a module will consist of a compiled specification (note this will be a serialized data structure rather than any generated code) and the implementation along with module-specific resources. The implementation will not have to be all in one language, but will be able to be a melange - for example a external exposing mobile functionality might be written in Obj-C for iOS, Java for Android and LiveCode for the other platforms (the latter perhaps just providing no-ops, or a simple emulation). The engine will use the information in the specification to work out what the syntax is for the module, what method (in whatever language - I'm using 'method' here as an umbrella term to describe a function/handler/static method/etc. that is present in the implementation at the level below LiveCode script) to call when and with what (native) types. The goal is that the only code you have to write is code directly related to providing the functionality - in particular, no messing about with type checking or type conversion.

Originally I had planned for 'just' using the typing information from the implementation language to define (at least) the bindings to syntax. However, this is woefully insufficient - after a fair bit of analysis and pondering it became clear that the LiveCode 'type environment' is a great deal richer than just strings, numbers and arrays. Indeed, here is a summary of the current range of types that LiveCode syntax we currently have actually uses:
  • The type ecosystem at the script level naturally splits up into the following collection of types:
  • any - the umbrella type, which can be anything
  • boolean - a type consisting of two values true or false
  • number - a real or integral number
  • integer - an integral number
  • unsigned integer - an integral number >= 0
  • character - a string of length ‘1’ (essentially a Unicode codepoint or native equivalent)
  • byte - a binary string of length 1
  • string - an arbitrarily long sequence of characters
  • binary string - an arbitrarily long sequence of bytes
  • map - an associative mapping between strings and values of any type (here the keys are considered to be case-sensitive)
  • dictionary - an associative mapping between names and values of any type (here the keys are considered to be case-preserving but case-insensitive)
  • array - a sequence of values
  • items - a sequence of strings separated by ‘,’
  • lines - a sequence of strings separated by the return character
  • enum - a family of types where for each type a value can be one of a fixed number of strings
  • set - a family of types where for each type a value can be a collection of a fixed number of strings
  • record - a family of types where for each type a value is a mapping from a fixed set of strings to values of any type
  • variant - a family of types where for each type a value can be one a of a fixed set of other types
Most of these types can also be annotated to make them more specific. In particular:
  • numeric types can be restricted to a particular range
  • sequence and array types can be restricted to hold values of only a specific type
  • sequence types can be restricted to be of a certain length
  • record types can be restricted so that specific elements can only be of a specific type
In addition to this there is also the idea of an 'opaque' type, which represents something which has no script-level representation (and thus is only used as the result of methods and immediately a parameter of another method), or something which has a mapping to some other script type but is much better presented to the 'native' method as a 'native' type. (There's also another type waiting to be added to the list the 'tuple' - a comma-separated list of values of differing types - we noticed this yesterday whilst going reviewing some things).

Now, as it stands for the version of the engine that will appear before we replace the parser (we're aiming to get an engine which has 'identical' functionality to now, but using the new split between evaluation of parameters and implementation methods) the specification files will not contain syntax (yet), just a description of the methods a module exposes from the point of view of LiveCode script types. For example, here is the spec for the functionality provided by what-will-be the 'printing' module (note this is for exposition, it isn't quite yet final - also this module doesn't use out/inout parameters nor some of the more complicated types, such as records):

Code: Select all

module Printing

import Interface.Point
import Interface.Rectangle
import Interface.Stack
import Interface.Card

set PrinterFeatures
  "collate" is Collate
  "copies" is Copies
  "color" is Color
  "duplex" is Duplex
end set

enum PrinterPageOrientation
  "portrait" is Portrait
  "reverse portrait" is ReversePortrait
  "landscape" is Landscape
  "reverse landscape" is ReverseLandscape
end enum

enum PrinterJobDuplex
  "none" is Simplex
  "short edge" is ShortEdge
  "long edge" is LongEdge
end enum

enum PrinterLinkType
  "" is Unspecified
  "anchor" is Anchor
  "url" is URI
end enum

enum PrinterBookmarkInitialStateType
  "open" is true
  "closed" is false
end enum

opaque PrinterPageRange
  "" is All
  "all" is All
  "current" is Current
  "selection" is Selection
  string is Custom
end opaque

opaque PrinterDeviceOutput
  "device" is Device
  "preview" is Preview
  "system" is System
  string is Custom
end opaque

command AnswerPageSetup(in as_sheet is boolean)
command AnswerPrinter(in as_sheet is boolean)

command OpenPrinting()
command OpenPrintingWithDialog(in as_sheet is boolean)
command OpenPrintingToDestination(in destination is string, in filename is string, in options is optional array)
command ClosePrinting()
command CancelPrinting()
command ResetPrinting()

command PrintAnchor(in name is string, in location is Point)
command PrintLink(in type is PrinterLinkType, in area is rectangle)
command PrintNativeBookmark(in title is string, in location is Point, in level is unsigned integer, in initially_closed is boolean)
command PrintUnicodeBookmark(in title is binary string, in location is Point, in level is unsigned integer, in initially_closed is boolean)
command PrintBreak()
command PrintAllCards(in target is optional Stack, in only_marked is boolean)
command PrintRectOfAllCards(in target is optional Stack, in only_marked is boolean, in from is Point, in to is Point)
command PrintCard(in target is optional Card)
command PrintRectOfCard(in target is optional Card, in from is Point, in to is Point)
command PrintSomeCards(in count is unsigned integer)
command PrintRectOfSomeCards(in count is unsigned integer, in from is Point, in to is Point)
command PrintRectOfCardIntoRect(in target is optional Card, in src_from is Point, in src_to is Point, in dst_rect is Rectangle)

global readonly property PrintDeviceFeatures is PrinterFeatures
global property PrintDeviceOutput is PrinterDeviceOutput
global property PrintPageOrientation is PrinterPageOrientation
global property PrintJobRanges is PrinterPageRange
global property PrintPageSize is items(2) of integer
global property PrintPageScale is number
global readonly property PrintPageRectangle is Rectangle
global property PrintJobName is string
global property PrintJobCopies is integer
global property PrintJobCollate is boolean
global property PrintJobColor is boolean
global readonly property PrintJobPage is integer
global property PrintJobDuplex is PrinterJobDuplex
global readonly property PrintDeviceRectangle is Rectangle
global property PrintDeviceSettings is binary string
global property PrintDeviceName is string
global property PrintCardBorders is boolean
global property PrintGutters is items(2) of integer
global property PrintMargins is items(4) of integer
global property PrintRowsFirst is boolean
global property PrintScale is number
global property PrintRotated is boolean
global property PrintCommand is string
global property PrintFontTable is string
global readonly property PrinterNames is string

end module
So, the specification file above provides a language-neutral (from an implementation point of view) description of how script will interact with the implementation methods. The other piece of the puzzle is the type information from the implementation - now, in Java this will be extractable using the reflection mechanism, however as C/C++ doesn't provide this ability we've written a tool (that uses gccxml) that parses a header file for the module and generates the necessary information (note this is for exposition, it isn't quite yet final):

Code: Select all

#ifndef __MC_EXEC_PRINTING__
#define __MC_EXEC_PRINTING__

#ifndef __MC_SYSDEFS__
#include “sysdefs.h”
#endif

#ifndef __MC_INTERFACE__
#include “interface.h”
#endif

typedef intset_t MCPrintingPrinterFeatures;
enum /* MCPrintingPrinterFeatures */
{
	kMCPrintingPrinterFeatureCollate,
	kMCPrintingPrinterFeatureCopies,
	kMCPrintingPrinterFeatureColor,
	kMCPrintingPrinterFeatureDuplex,
};

enum MCPrintingPrinterPageOrientation
{
	kMCPrintingPrinterPageOrientationPortrait,
	kMCPrintingPrinterPageOrientationReversePortrait,
	kMCPrintingPrinterPageOrientationLandscape,
	kMCPrintingPrinterPageOrientationReverseLandscape,
};

enum MCPrintingPrinterJobDuplex
{
	kMCPrintingPrinterJobDuplexNone,
	kMCPrintingPrinterJobDuplexShortEdge,
	kMCPrintingPrinterJobDuplexLongEdge,
};

enum MCPrintingPrinterLinkType
{
	kMCPrintingPrinterLinkTypeUnspecified,
	kMCPrintingPrinterLinkTypeAnchor,
	kMCPrintingPrinterLinkTypeURI,
};

//////////

enum MCPrintingPrinterPageRangeRepType
{
	kMCPrintingPrinterPageRangeRepTypeAll,
	kMCPrintingPrinterPageRangeRepTypeCurrent,
	kMCPrintingPrinterPageRangeRepTypeSelection,
	kMCPrintingPrinterPageRangeRepTypeCustom,
};

struct MCPrintingPrinterPageRangeRep
{
	MCPrintingPrinterPageRangeRepType type;
	union
	{
		MCStringRef custom;
	};
};

struct MCPrintingPrinterPageRange;

void MCPrintingPrinterPageRangeFree(MCExecContext& ctxt, MCPrintingPrinterPageRange& value);
void MCPrintingPrinterPageRangeCopy(MCExecContext& ctxt, MCPrintingPrinterPageRange& src_value, MCPrintingPrinterPageRange& dst_value);
void MCPrintingPrinterPageRangeEncode(MCExecContext& ctxt, MCPrintingPrinterPageRangeRep& rep, MCPrintingPrinterPageRange& value, MCExecErrorInfo*& r_error);
void MCPrintingPrinterPageRangeDecode(MCExecContext& ctxt, MCPrintingPrinterPageRange& value, MCPrintingPrinterPageRangeRep& rep);

//////////

enum MCPrintingPrinterDeviceOutputRepType
{
	kMCPrintingPrinterDeviceOutputRepTypeDevice,
	kMCPrintingPrinterDeviceOutputRepTypePreview,
	kMCPrintingPrinterDeviceOutputRepTypeSystem,
	kMCPrintingPrinterDeviceOutputRepTypeCustom,
};

struct MCPrintingPrinterDeviceOutputRep
{
	MCPrintingPrinterDeviceOutputRepType type;
	union
	{
		MCStringRef custom;
	};
};

struct MCPrintingPrinterDeviceOutput;

void MCPrintingPrinterDeviceOutputFree(MCExecContext& ctxt, MCPrintingPrinterDeviceOutput& value);
void MCPrintingPrinterPageRangeCopy(MCExecContext& ctxt, const MCPrintingPrinterDeviceOutput& src_value, MCPrintingPrinterDeviceOutput& dst_value);
void MCPrintingPrinterPageRangeEncode(MCExecContext& ctxt, MCPrintingPrinterDeviceOutputRep& rep, MCPrintingPrinterDeviceOutput& value, MCExecErrorInfo*& r_error);
void MCPrintingPrinterPageRangeDecode(MCExecContext& ctxt, const MCPrintingPrinterDeviceOutput& value, MCPrintingPrinterDeviceOutputRep& rep);

//////////

void MCPrintingExecAnswerPageSetup(MCExecContext &ctxt, bool p_is_sheet);
void MCPrintingExecAnswerPrinter(MCExecContext &ctxt, bool p_is_sheet);

void MCPrintingExecCancelPrinting(MCExecContext& ctxt);
void MCPrintingExecResetPrinting(MCExecContext& ctxt);
void MCPrintingExecPrintAnchor(MCExecContext& ctxt, MCStringRef name, MCPoint location);
void MCPrintingExecPrintLink(MCExecContext& ctxt, int type, MCStringRef target, MCRectangle area);
void MCPrintingExecPrintNativeBookmark(MCExecContext& ctxt, MCStringRef title, MCPoint location, integer_t level, bool initially_closed);
void MCPrintingExecPrintUnicodeBookmark(MCExecContext& ctxt, MCStringRef title, MCPoint location, integer_t level, bool initially_closed);
void MCPrintingExecPrintBreak(MCExecContext& ctxt);
void MCPrintingExecPrintAllCards(MCExecContext& ctxt, MCStack *stack, bool only_marked);
void MCPrintingExecPrintRectOfAllCards(MCExecContext& ctxt, MCStack *stack, bool p_only_marked, MCPoint from, MCPoint to);
void MCPrintingExecPrintCard(MCExecContext& ctxt, MCCard *target);
void MCPrintingExecPrintRectOfCard(MCExecContext& ctxt, MCCard *target, MCPoint from, MCPoint to);
void MCPrintingExecPrintSomeCards(MCExecContext& ctxt, integer_t count);
void MCPrintingExecPrintRectOfSomeCards(MCExecContext& ctxt, integer_t count, MCPoint from, MCPoint to);
void MCPrintingExecPrintCardIntoRect(MCExecContext& ctxt, MCCard *card, MCRectangle destination);
void MCPrintingExecPrintRectOfCardIntoRect(MCExecContext& ctxt, MCCard *card, MCPoint from, MCPoint to, MCRectangle destination);

void MCPrintingExecClosePrinting(MCExecContext& ctxt);

void MCPrintingExecOpenPrintingToDestination(MCExecContext& ctxt, MCStringRef p_destination, MCStringRef p_filename, MCArrayRef p_options);
void MCPrintingExecOpenPrinting(MCExecContext& ctxt);
void MCPrintingExecOpenPrintingWithDialog(MCExecContext& ctxt, bool p_as_sheet);

void MCPrintingGetPrinterNames(MCExecContext& ctxt, MCStringRef& r_printers);

void MCPrintingGetPrintDeviceFeatures(MCExecContext& ctxt, MCPrintingPrinterFeatures& r_features);
void MCPrintingSetPrintDeviceOutput(MCExecContext& ctxt, const MCPrintingPrinterDeviceOutput& output);
void MCPrintingGetPrintDeviceOutput(MCExecContext& ctxt, MCPrintingPrinterDeviceOutput& r_output);
void MCPrintingGetPrintDeviceRectangle(MCExecContext& ctxt, MCRectangle &r_rectangle);
void MCPrintingGetPrintDeviceRectangle(MCExecContext& ctxt, MCRectangle &r_rectangle);
void MCPrintingGetPrintDeviceSettings(MCExecContext& ctxt, MCStringRef &r_settings);
void MCPrintingSetPrintDeviceSettings(MCExecContext& ctxt, MCStringRef p_settings);
void MCPrintingGetPrintDeviceName(MCExecContext& ctxt, MCStringRef &r_name);
void MCPrintingSetPrintDeviceName(MCExecContext& ctxt, MCStringRef p_name);

void MCPrintingGetPrintPageOrientation(MCExecContext& ctxt, MCPrintingPrinterPageOrientation& r_orientation);
void MCPrintingSetPrintPageOrientation(MCExecContext& ctxt, MCPrintingPrinterPageOrientation orientation);

void MCPrintingSetPrintJobRanges(MCExecContext& ctxt, const MCPrintingPrinterPageRange& p_ranges);
void MCPrintingGetPrintJobRanges(MCExecContext& ctxt, MCPrintingPrinterPageRange& r_ranges);

void MCPrintingSetPrintPageSize(MCExecContext& ctxt, integer_t p_value[2]);
void MCPrintingGetPrintPageSize(MCExecContext& ctxt, integer_t r_value[2]);
void MCPrintingSetPrintPageScale(MCExecContext& ctxt, double p_value);
void MCPrintingGetPrintPageScale(MCExecContext& ctxt, double &r_value);
void MCPrintingGetPrintPageRectangle(MCExecContext& ctxt, MCRectangle &r_value);

void MCPrintingGetPrintJobName(MCExecContext& ctxt, MCStringRef &r_value);
void MCPrintingSetPrintJobName(MCExecContext& ctxt, MCStringRef p_value);
void MCPrintingGetPrintJobCopies(MCExecContext& ctxt, integer_t &r_value);
void MCPrintingSetPrintJobCopies(MCExecContext& ctxt, integer_t p_value);
void MCPrintingGetPrintJobDuplex(MCExecContext& ctxt, MCPrintingPrintJobDuplex& r_value);
void MCPrintingSetPrintJobDuplex(MCExecContext& ctxt, MCPrintingPrintJobDuplex p_value);
void MCPrintingGetPrintJobCollate(MCExecContext& ctxt, bool &r_value);
void MCPrintingSetPrintJobCollate(MCExecContext& ctxt, bool p_value);
void MCPrintingGetPrintJobColor(MCExecContext& ctxt, bool &r_value);
void MCPrintingSetPrintJobColor(MCExecContext& ctxt, bool p_value);
void MCPrintingGetPrintJobPage(MCExecContext& ctxt, integer_t &r_value);

void MCPrintingGetPrintCardBorders(MCExecContext& ctxt, bool &r_card_borders);
void MCPrintingSetPrintCardBorders(MCExecContext& ctxt, bool p_card_borders);
void MCPrintingGetPrintGutters(MCExecContext& ctxt, integer_t r_gutters[2]);
void MCPrintingSetPrintGutters(MCExecContext& ctxt, integer_t p_gutters[2]);
void MCPrintingGetPrintMargins(MCExecContext& ctxt, integer_t r_margins[4]);
void MCPrintingSetPrintMargins(MCExecContext& ctxt, integer_t p_margins[4]);
void MCPrintingGetPrintRowsFirst(MCExecContext& ctxt, bool &r_rows_first);
void MCPrintingSetPrintRowsFirst(MCExecContext& ctxt, bool p_rows_first);
void MCPrintingGetPrintScale(MCExecContext& ctxt, double &r_scale);
void MCPrintingSetPrintScale(MCExecContext& ctxt, double p_scale);
void MCPrintingGetPrintRotated(MCExecContext& ctxt, bool &r_rotated);
void MCPrintingSetPrintRotated(MCExecContext& ctxt, bool p_rotated);
void MCPrintingGetPrintCommand(MCExecContext& ctxt, MCStringRef &r_command);
void MCPrintingSetPrintCommand(MCExecContext& ctxt, MCStringRef p_command);
void MCPrintingGetPrintFontTable(MCExecContext& ctxt, MCStringRef &r_table);
void MCPrintingSetPrintFontTable(MCExecContext& ctxt, MCStringRef p_table);

#endif
With this information (suitable encoded), the engine can then work out how to map between the (abstract, implementation language-neutral) script-level definition of the method to the actual implementation - in whatever language it might be.

(I perhaps should stress that the above example doesn't really show the full richness of the type mapping we are going for - for example, native methods will be able to specify parameters as being things like 'MCMutableStringRef', which can map to an 'inout string' parameter at the script-level. This means that, wherever possible, that method will get (essentially) direct access to a variable's contents to modify - there won't need to be any copying.)

Over time the specification file will be expanded to not just hold the method descriptions, but also the syntax that will be used to call them and reference documentation for the syntax itself.

Just paying reference to the desire for 'pure Java externals with no external tools needed' then, yes, eventually you probably could produce a tool that generated the necessary information from class files - although I do think that relying on annotations would make things much harder to read than a cohesive 'LiveCode-script level' spec (particularly for the structured entities that you can specify, and for syntax which will in many cases required auxillary clauses)... Also given that other aspects of Java development (like C/C++ etc.) require tools to process interface definitions (in particular if you want to interact with CORBA - you write an IDL file and use idlj) - there is already precedent. In the end, if the result of writing a specification file and having to compile it in some way gives you a great deal more than you could get without it then I don't think anyone is going to mind too much.

Janschenkel
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 977
Joined: Sat Apr 08, 2006 7:47 am
Location: Aalst, Belgium
Contact:

Re: android externals

Post by Janschenkel » Tue Jul 09, 2013 11:18 am

@monte
I know what you mean - I want to push it forward too - alas, my telepathy skills are a bit rusty or I would have used them on Mark Waddingham to figure out where he wanted to take this ;-)

All jokes aside, right after the conference I took a first tour along the externals code, but my brain froze at one point and I need to take that tour again to properly map things out into my head. So please take the ramblings that follow as "not completely thought through yet."

There are in essence two aspects: (1) getting from the engine into the externals code, and (2) getting from the externals code back into the engine via callbacks when needed.

(1) getting from the engine into the externals code
- every external package load creates a counterpart object in the engine which loads the XTable to get at the external command and functions
- when an 'unknown' command or function call makes it way to the engine, it looks in its external packages to see which one can handle the call
- then it calls the actual code and shuffles the result back into the script execution object
- here we could distinguish between 'native' and 'virtual machine' externals
-> when a native external command or function is executed the engine can directly call into its code
-> when a virtual machine external command or function is executed the engine asks the appropriate virtual machine to execute the code

(2) getting from the externals code back into the engine via callbacks when needed
- here we have different flavors at the moment:
-> the 'classic' subset of the HyperCard external interface,
-> the 'never released' interface which was planned for version 4.0,
-> the 'iOS' externals sdk
-> and then the [[ExternalsApiV5]] stuff that Mark Waddingham has been working on
- this is what needs to be unified into a coherent model
-> the 'classic' and 'iOS' externals can be handled in a compatibility layer at that point
- again we can distinguish between 'native' and 'virtual machine' externals
-> when a native external needs to call back into the engine it can do so directly
-> when a virtual machine external needs to call back into the engine it can do it indirectly via an external interface object in the virtual machine which knows how to tap into the engine

The goal should be that the process is transparent, both to the external producer and the external consumer.
If I produce a Java or C# external, I don't want to be bothered with the details of Java Native Interface or CLR Hosting API.
If I consume a Java or C# external, I certainly don't want to be bothered with these details (once I've told LiveCode where it can find the JVM or .NET version).

If all this means we need .lcidl files after all then I'm certainly not going to oppose it, and champion annotation/attribute processors to take the drudgery out of it. But given the power of reflection API's in both Java and .NET, I don't think it's necessary.

Jan Schenkel.
Quartam Reports & PDF Library for LiveCode
www.quartam.com

monte
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 1564
Joined: Fri Jan 13, 2012 1:47 am
Contact:

Re: android externals

Post by monte » Tue Jul 09, 2013 11:42 am

Thanks for the detailed explanation @runrevmark. Very interesting to see where it's all going. BTW I found an implementation of what I'm talking about with intents:
http://www.cs.utep.edu/cheon/cs4390/dat ... y-java.txt
LiveCode User Group on Facebook : http://FaceBook.com/groups/LiveCodeUsers/

LCMark
Livecode Staff Member
Livecode Staff Member
Posts: 1206
Joined: Thu Apr 11, 2013 11:27 am

Re: android externals

Post by LCMark » Tue Jul 09, 2013 11:54 am

@Janschenkel: As I said above, with the path the refactored engine is taking, externals go away - all engine functionality whether it comes with the core package, or provided by third-parties will be provided by modules which are essentially externals-on-steroids from the point of view of comparison to what you can do now with externals to what you will be able to do.
getting from the engine into the externals code
The specification file (or compiled form - however that is generated) will tell the engine what methods (and eventually syntax) the module exports that script can utilize. This will also tell the engine where the methods reside (implementation-wise) - e.g. in a machine code blob, a Java class file, a LiveCode script. It will also describe the required type mappings. Now, obviously the engine will need to have an extensible 'language-binding' layer so that support for other languages can be added and this binding layer will be responsible for mapping script-level types to native types (essentially what the generated code lcidlc creates does at the moment), calling the appropriate method in the implementation (in whatever Virtual Machine is necessary), and providing an appropriate wrapper around the core APIs that modules have access to.
getting from the externals code back into the engine via callbacks when needed
The interface here will be a far-richer variant of the current LC* API presented in Support.h for the lcidlc based externals. Indeed, you will be able to request access to certain 'services', each giving you an API for doing various things that the engine already has code for. This includes manipulating values, accessing the platform API layer, the graphics API layer. Again, the thing to remember is that we want all engine functionality to refactored into distinct modules with clear API boundaries - so the collection of services you'll have access to will be identical to that the current engine code does (albeit cleaned up substantially).

In terms of the current situation then:
  • The HyperCard API can be made into a shim around the new API system without too much difficulty. Indeed, it will likely be a module itself that holds the shim and code for loading externals, making them work the way they do now.
  • The 'raw' v1 interface will go away - externals written using the lcidlc mechanism will just need to be recompiled with a modified lcidlc which generates the appropriate module spec information. (I've been careful to ensure that the functionality provided by lcidl files is a subset of what will be).
Part of the reason why I've been trying to find the time to 'hack' in support for both desktop and Android externals via the lcidlc mechanism is that we can make it forward-compatible with just a recompile needed at some-point down the line.

I'm all for making things as simple as possible for the module writer - which is the main motivation for making the script-level type system and mapping rules as rich as possible... The more the engine can do in this regard, the less glue code the module writer has to write and the more uniform and consistent the functionality provided by modules becomes.
Last edited by LCMark on Tue Jul 09, 2013 12:00 pm, edited 1 time in total.

Janschenkel
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 977
Joined: Sat Apr 08, 2006 7:47 am
Location: Aalst, Belgium
Contact:

Re: android externals

Post by Janschenkel » Tue Jul 09, 2013 11:56 am

@runrevmark
Thanks for the clarification, Mark. My thinking was a tad myopic as I was focusing on the part I'm trying to solve rather than the big picture of LiveCode extension modules. And it confirms that .lcidl files (or their successors) have a big role to play.
As for annotations versus separate definition files: since version 5 things in the Java world have shifted towards annotating code rather than filling in a myriad of configuration files. In our case, the annotations could be used to help with the creation of the .lcidl files; but probably can't serve as a replacement - unless we add another slew of annotations anyway ;-)

Jan Schenkel.
Quartam Reports & PDF Library for LiveCode
www.quartam.com

LCMark
Livecode Staff Member
Livecode Staff Member
Posts: 1206
Joined: Thu Apr 11, 2013 11:27 am

Re: android externals

Post by LCMark » Tue Jul 09, 2013 11:59 am

@monte: Meant to post a reply yesterday, but had to go to a meeting and ran out of time after that... So...
Thinking about things some more and I realised how limited an android externals sdk will be without the ability to handle intent results. So I think we need some way to register a method to handle an intent. One way I'm thinking is if the externals api has a method... startIntent(Intent,LCIntentHandler) so the engine's java side starts the intent with a requestCode it has mapped to the LCIntentHandler object. When onActivityResult is invoked the LCIntentHandler object's intentResult method is invoked with the intent object.
Yes - we will need to tweak the engine here a bit and add an API to access them. One question I have is that (in practice) do intent requests actually need to be nested / overlap? All the uses of them we have at the moment are of the form:

Code: Select all

   start activity
   wait for result
   return activity intent result
Which leads to quite a nice API call (along the lines of):

Code: Select all

LCIntentRun(<intent>, out <result>)
Here, the engine would switch to the appropriate thread, call the intent, wait for its completion and return the result. Obviously a callback mechanism could be used too - but that would mean you'd need to explicit Wait and such as appropriate.

monte
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 1564
Joined: Fri Jan 13, 2012 1:47 am
Contact:

Re: android externals

Post by monte » Tue Jul 09, 2013 1:01 pm

Hmm... I think that covers most of the use cases I can think of. It looks like dropbox sdk gets you to handle onResume but I think that could be handled by resume message and then some command to call... although it is less flexible than what I was thinking
LiveCode User Group on Facebook : http://FaceBook.com/groups/LiveCodeUsers/

Locked

Return to “Engine Contributors”