Smart Mobile Studio
  • News
  • Forums
  • Download
  • Store
  • Showcases
    • Featured demos
    • The Smart Contest 2013, Round 1 – Graphics
  • Documentation
    • Get the book
    • System requirements
    • Prerequisites
    • Getting started
      • Introduction
      • Application architecture
      • The application object
      • Forms and navigation
      • Message dialogs
      • Themes and styles
    • Project types
      • Visual project
      • Game project
      • Console project
    • Layout manager
    • Networking
      • TW3HttpRequest
      • TW3JSONP
      • Loading files
  • About

Author Archives: Jon Lennart

SPNG: Be informed when values change

Posted on 08.04.2017 by Jon Lennart Posted in Developers log, News, News and articles 2 Comments

There are many new and powerful features in the upcoming release of Smart Mobile Studio (also refered to as Smart Pascal, Next Generation: SPNG in short): One of the most powerful is the ability to be notified when a value or object changes with a single line of code.

So far TVariant has just been a helper class for dealing with typical tasks. Well, perhaps not just typical because under Smart Pascal, variants can map directly to a javascript object (of any type) and allows you direct access to its prototype. The Handle property that all visual controls have is an example of this. The type TControlHandle is actually a reference to the visual element inside the DOM (document object mode). So by accessing that directly in your code, you can change styles, attributes and properties of that element. You are not bounds to only use our pre-fabricated solutions.

In the next update TVariant have a lot of new members. Two that stands out as important: watch() and unwatch().

As their name imply they will warch something for you. In this case you wil be notified whenever a single property, a whole object or indeed – a property deep within an object is altered. It may sound trivial but it’s actually one of those features that lays the foundation for responsive, data-aware controls.

But you can use it for many other things as well. For example you can now attach an event directly to a string. And whenever that string changes (either by your code, the user, or some other script) you can act on it directly. That is pretty cool and very helpful!

  // create an empty javascript object
  var MyValue: Variant := TVariant.CreateObject();

  // Set a default value
  MyValue.NewProperty := 12; // add a property and set a value

  // We want to know when the object changes
  TVariant.Watch(MyValue, "NewProperty", procedure ()
    begin
      showmessage("You changed the value!");
    end);

In this snippet we setup a javascript object and place a named-property inside that object. We then listen for changes to that property (by name even). So if you change that value further down in the code, or by calling some procedure elsewhere – the event will fire and show the message dialog.

This is of-cource just a tiny fragment of the new code that has been added, not to mention the changes and fixes. Some of these features are presently in alpha stage and is being tested. We will post more and more news in the days and weeks ahead – and we are sure you will be pleased with some the fantastic new things you can now code!

Here is an example of the new desktop and windowing applications you can write (picture below): The smart desktop!

The Smart desktop

We decided to update the uartex Media Desktop, an example initially made for touch based embedded environments – and turned it into a fully functional desktop environment!

Both the desktop itself, the filesystem, the IO drivers and windowing toolkit is written in Smart Pascal. You can create kiosk software designed to run on embedded devices, advanced applications designed to run on your intranet – or full cloud systems. So it can run a Smart application inside each Window if you like. But you have full control over windows, file access, directly listings, menu items and can sculpt both the desktop and applications to suit your needs.

The desktop can be coupled with a node.js backend that handles multi-user login and file access. It gives your users access to designated home-folders (again under your control). So each user can have their own work files just like a thin client, and each user can run different applications from a fully working desktop environment.

Below you are seeing Quake II compiled from C/C++ to LLVM bitcode. This is in turn compiled to JavaScript via asm.js. This means the browser will compile the javascript into machine code after loading. So Quake II runs at native speed, all JavaScript, inside our desktop window.

Create windowed applications with Smart Desktop

Create windowed applications with Smart Desktop

You will also be happy to head that x86 Linux and ARM kiosk distros have been created. So once happy with your desktop you can in fact boot straight into it in fullscreen. No Linux desktop or shell — just your desktop running. Both Firefox and Chrome can be used as presenter.

Have a great easter!

announcement code demo HTML5 javascript Object Pascal OP4JS release Smart Mobile Studio Upcoming features

JavaScript hardware, books and the long awaited update

Posted on 03.11.2016 by Jon Lennart Posted in News 1 Comment
Worth every penny!

Worth every penny!

If you head over to Manning publishing you will find a very interesting book called Javascript on things, written by Lyza Danger Gardner. As mentioned both here as well as Jon’s developer personal developer blog (*) on numerous occasions – more and more hardware is capable of running JavaScript. Or to be more precise: they use JavaScript as their primary automation language.

There is actually a whole market where you as a Smart Pascal developer can use your skills to deliver top of the line products. A market where archetypical languages like C++ and Delphi makes less sense. IOT is about adapting to technology quickly, pivoting on a dime, connecting hardware together in new and exciting ways. In such a market script engines makes more sense than heavy-duty native languages.

If this inspires you then have a look at JavaScript of things, it is one of those books you can return to again and again.

Smart Pascal and IOT

While we love JavaScript at The Smart Company we created Smart Pascal for a reason, and that was to give you the best of both worlds. Smart Pascal gives you a huge advantage over vanilla JavaScript.

The ability to write proper classes with traditional inheritance just like you do in C++ or Delphi is one of them. While JavaScript developers will argue that JS is object-oriented, it is not really true; at least not in the traditional sense. A JavaScript object has no VMT (virtual method table) and instead relies on something called prototypes. Which essentially is an object that can be expanded with new properties at any time, and properties can be both values and methods.

Inheritance is typically emulated through cloning of these prototypes. This is extremely inefficient and cause compatibility problems between frameworks and libraries. This is why standards such as require.js has become so important for JavaScript development because it provides structure. The same structure and order languages like object pascal and C++ gives you out of the box.

Smart Pascal doesn’t need things like require.js or similar standards to deliver order and safe code. Nor do we need to clone prototypes en-mass to fake what is already an intrinsic part of the language. Our compiler organize and sculpts a VMT in JavaScript itself and does exactly what C++ and Delphi does. Namely to isolate an objects data (the fields of your class) in a Self record. A self record that is passed along to class calls as the first parameter automatically. This completely removes the need for low-level cloning of entire objects. Class code is implemented once, and only the self record is initialized when you create new instances. So if you create 10 instances of a class you don’t get 10 copies of all your methods, all your properties and so on in memory. Instead you get 10 instance records (self) and your code is compiled once. This is the way of things, the way of the force .. 😉

So believe it or not, but traditional object orientation in itself an advantage over traditional JavaScript. Then you get all the other perks of a real, archetypical programming language on top of that: interfaces, partial classes, virtual and abstract methods, helper classes, operator overloading, unit organization, a wast runtime library of classes and visual controls – and much, much more!

Most Delphi or C++ developers don’t really think about these things. We are so used to proper inheritance and VMT operations that we regard them as fundamental. Which is also why many C++ and Delphi developers dont like to work in JavaScript. They get the sense that JavaScript is sort of half-finished. Which is actually true! The author of JavaScript never got to finish it, so the prototype system in JavaScript was never intended to be publicly exposed. He was aiming for object inheritance like Smart Pascal and TypeScript delivers – but sadly he never got to finish that part.

Using Smart Pascal to work with JavaScript enabled hardware

Smart Mobile Studio has supported this for quite some time. You can create Espruino projects if you need to work with micro controllers. If you look in the RTL directory there is a folder named Espruino, and it contains pascal versions of the Espruino JavaScript objects. It really gives you a huge advantage over bare-metal JavaScript.

Then there is node.js, which is essentially the engine most hardware vendors add to their SoC to JavaScript enable their devices. Node.js has been supported since version 2.0, and allows you to write a whole host of platform independent projects. You can write Linux servers, Linux Services (daemons as they are called), Windows services — anything you can think of really.

But naturally, you need to get a book on node.js and Espruino to know how to use these project-types properly. But they are all there and once you get into node.js and what you can do, the world really is your oyster.

The next update to Smart Mobile Studio

We are working very hard on getting the next version of Smart Mobile Studio ready for x-mas. While there will be bug fixes to the IDE (and a few new features as well), this update is primarily an update for the RTL and run-time library. We have tried very hard to keep things compatible with previous versions, but there will be changes that requires a bit of editing. But believe me, it will be worth it.

Here are some of the highlights:

  • Better fragmentation of classes and unit files
  • Namespace orientation
  • New visual components
  • Unified filesystem classes
  • A full overhaul of all visual controls
  • Plenty of bug fixes
  • Faster execution time
  • Better resource handling
  • Wiki documentation!

Stay tuned for more in-depth look at all the new classes and the features they provide.

As a bonus, Jon Lennart Aasenden have decided to give his upcoming book, Smart Mobile Studio Unleashed, for free to registered customers. The book will retail for $33 on Amazon. So if you want to learn the inner secrets of the VJL (visual JavaScript component library), how to write kick-ass custom controls – all the way to scalable, cluster based node.js services, this is THE book to get.

(*) the statements and articles on Jon’s personal devlog is not affiliated with this company. His blog is a personal blog which covers a variety of topics.

Book Espruino Inheritance IOT javascript JavaScript of things node.js Update

Partial classes, the smart way

Posted on 25.10.2016 by Jon Lennart Posted in Developers log, Documentation, News

Smart Mobile Studio have had support for partial classes for quite some time. It’s already an integral part of our run-time library and brings invaluable features to how we extend and work with the base system. But for those new to this concept, a short introduction is in order.

What is a partial class?

A partial class is, essentially, that you divide the implementation of a class into smaller pieces. These pieces can reside in the same unit or across multiple units of your codebase. When the compiler encounters a class marked as partial, it will wait until the compilation is complete before it seals the virtual method table for that class.

That sounds very technical, but it’s really super easy to use and you dont have to get into the nitty-gritty of things to take advantage of the fantastic new abilities it gives you.

You may ponder what on earth is the use for something like this? Well, first of all it allows you a finer grain of control when dealing with multi-platform support. Let’s say you have some code that you need to work on both visual (SmartCL namespace) client applications and server-side (node.js) as well. Naturally SmartCL depends on the document object model (DOM) being present, but under node.js there is no such thing. If you try to use the units prefixed with SmartCL.* under node.js, it will either crash the server or produce some spectacular access violations.

With partial classes you can solve this quite elegantly by putting the base-class in one unit, and then finish the class in separate units: one for browser-based code and the other for node.js based environments.

Extending functionality

But the power of partial classes is not really in organization. The above example could just as easily be solved with some clear-cut compiler defines. Not as elegant, but just as effective. The real power is in how partial classes can extend any class which is defined as partial. Like TW3CustomControl, which is the foundation for all visual controls.

Let’s say you want to add a function for fading out any control. Not just a single control, but all controls (since all visual control inherit from TW3CustomControl, any addition to that class affects all descendants equally). If you take a look at the class (which is found in SmartCL.Components.pas) you will notice that it’s marked as partial.

  TW3CustomControl = partial class(TW3MovableControl)
  ..
  ..
  end;

That means that we can extend the class at any time. So if we add the following code to one of our units:

type
  TW3CustomControl = partial class(TW3MovableControl)
  public
    procedure FadeOut;
  end;

procedure TW3CustomControl.FadeOut;
begin
  // Add code here
end;

Hit compile so the compiler picks up on this, and voila – the code suggestion will now show the new member FadeOut() for all controls. Every control that inherits from TW3CustomControl gets this new member. That is pretty cool! You could ofcourse do something similar with a class helper (which Smart Mobile Studio also supports), but the difference between a class-helper and a partial class is quite radical.

A class helper is not a true member of a class, it’s more (as the name implies) a helper that operates around an instance. But a partial class is the class itself, with full access to protected and private members. You have just told the compiler that “oh by the way, add this to TW3CustomControl”. Any class defined as partial will not have it’s VMT sealed until the very last moment.

Need to extend that websocket client you find lacking? Well, if it’s marked as partial you can extend the class as you see fit without messing about with the RTL. It’s also a good way to extend the copyrighted RTL with new features that can be sold and distributed as a package. You wont violate anything because you are using partial classes!

Incidentaly this is how Mono C# solved the problem of targeting Android and Apple iOS from the same codebase. We have done more or less the same in our upcoming RTL where we introduce 3 distinct namespaces. So there is a lot of goodies in the bag for x-mas 🙂

Effects

If you add the unit SmartCL.Effects.pas to your project you will notice something very cool! Namely that TW3CustomControl suddenly get’s around 30 new members. These are special effects methods that will help you write some truly amazing controls. Want a button to shake when the user touches it? No problem. Want a panel to fade out, or perhaps slide off to the right? Easy as apple pie. Just open up SmartCL.Effects.pas and have a look. You can even daisy-chain effects to each other (with callbacks and duration settings) and Smart will execute them in sequence (!)

Verdict

I hope this introduction to partial classes was helpful and that it enriches the way you write Smart solutions. Naturally there is more than one system for partial classes on the market. C# has a slightly different take on this technology. But personally and speaking from experience (I have worked quite a lot with Mono C#) I feel our variation on the theme is better. You have to type more in C# to achieve what Smart does in a couple of lines, and the level of control you have is just awesome.

And ofcourse: It makes it that much easier to deal with multi-platform code, which is what our next updates is all about.

Logistic problems

Posted on 21.10.2016 by Jon Lennart Posted in Announcements, News

We have been experiencing some logistic problems with our service desk for a while; Sadly we were only able to verify the magnitude of this today.
Apparently our Zendesk account have had problems forwarding messages, and as such – there is a substancial number of emails and messages that we quite frankly havent had access to until now.

We will be going through the backlog in the coming days and weeks, and we apologize to all that have contacted us without getting a proper response.

Regards

The Smart Mobile Studio Team

announcement

NodeJS server programming

Posted on 26.09.2016 by Jon Lennart Posted in Developers log 3 Comments

Work is progressing at a steady pace, and one of the aspects of the new Smart Mobile Studio is how the RTL is cleverly organized into namespaces.

Now it’s to early to spill the beans on all the new stuff, but in short we reached the point where we had to make some radical changes in order for the product to grow and reach its full potential. So far our RTL has aimed at the much loved VCL/LCL component architecture. This is not going to change at all, but how things are organized just had to be dealt with at some point. The longer we wait, the harder it’s going to be to introduce new features.

Namespaces

In the new model we have a clear distinction between visual and non-visual units. Visual units (or code in general) depends on the document object model (DOM) being present. As you probably know, non-visual environments like Node.js and IO.js doesnt have a document object model. These are primarily designed to run server code. They are also used to write actual system services (specially on Linux where turning a script into a background service is easy) or command line scripts.

Add to that technologies like Cordova Phonegap and Espruino (micro controller); then hybrid systems like nodewebkit, and you probably understand how important a clear separation of concepts and code is. But im not going into the nitty gritty of this now.

NodeJS and server side programming

Smart Mobile Studio have shipped with a node.js project type for some time. It throws you in at the deep-end of JavaScript programming, with nothing but the low-level header files to work with. This is fine if you are a JavaScript guru and know node intimately. But working on the level of headers and external declarations is not really user friendly. When you need to finish a server by next week, one that scales and can be clustered (which is just so powerful that I find it hard to describe just how cool this is) – you dont want to fiddle around with header files.

Well, soon you wont have to! In fact, as of writing I’m testing the first server type (http) and it’s performing brilliantly. Its a pleasure to write this part of the RTL !

Nodejs rocks! And with Smart Pascal, it rocks even more!

Nodejs rocks! And with Smart Pascal, it rocks even more!

A simple Node.js server

So, what does the most basic node.js web service look like? It’s almost to easy:

  var server := TNJHTTPServer.Create;
  Server.Port:=80;
  Server.OnAfterServerStarted:= procedure (sender: TObject)
    begin
      console.log("Server has started, listening on port:" + TNJHTTPServer(Sender).Port.toString);
    end;

  Server.OnRequest := procedure (Sender: TObject;
      const Request: TNJHttpRequest;
      const Response: TNJHttpResponse)
    begin
      console.log("-------------");
      console.log("Handling request:");
      console.log(request.Method);
      console.log(request.Url);
      console.log(request.Socket.remoteAddress);
      console.log("Listing headers:");
      for var x:=1 to request.headers.count do
      begin
        console.log(request.headers.Items[x-1]);
      end;
      console.log("-- OK");
      var LText:= "Your headers are:" + #13
        + Request.Headers.ToJSON() + #13
        + "All done.";
      response.end(Ltext);
    end;

  server.Start;

And that’s just one type of server. The really exciting stuff is when you dig into websocket and use socket.io to design your protocols (and clustering!). You can even setup events on both sides that trigger on spesific message types. Forget about heavy duty threading models — all of that is done for you. All you have to do is write the actual service.

Benefits of Node.js

Hosting node services costs next to nothing. Hosting providers are a dime a dozen and you can forget about those nifty prices you normally pay for pure executables (like under Delphi or Lazarus). So there is a huge financial aspect to take into consideration here as well, not just technical jargon. Node services can be moved, they are platform independent and couldnt care less if you use Linux or Windows, Amazon or Azure: as long as node is installed your good to go!

And last but not least: inter-communicating with existing services that are modern, and delivering solutions to your customers that talk JavaScript, Delphi, C++, C# or whatever their preference may be.

So good times ahead!

Cordova HTML5 javascript node.js nodejs rtl Smart Mobile Studio socket.io

That installer thing

Posted on 23.09.2016 by Jon Lennart Posted in Developers log

Smart Mobile Studio can be downloaded as a traditional installer-file for Microsoft Windows. This works quite well, especially when doing a clean install, and it would be very odd not to provide an installer in 2016.

However, we do get feedback from people that experience problems with this. Not the installer itself, but rather when they update their system by just installing on-top of an older installation, things become problematic. In short: depending on just how old that installation is, it can be plain sailing or a frustrating experience.

In newer versions of SMS you dont need to download a new installer

In newer versions of SMS you dont need to download a new installer

A while back we introduced the concept of “live updates”. In short Smart Mobile Studio ships with an automatic update application that makes sure you have the latest executables, the latest RTL and that your libraries are always fresh off the mint. Each version of Smart has its own channel inside the update program. The value of such a system is naturally that you dont have to keep on downloading installers, punch in the serial number – or that we accidentally overwrite or delete your own library files. The updater will only deal with the files known to it’s RTL, and leave your work in peace.

File IO is not just write and forget

Windows abound

Windows abound

Windows is not what it used to be. I personally think Windows has embraced the concept of users, access rights and credentials quite well (although Windows Vista made me leave the platform for Ubuntu Linux for a while). But all in all, Windows 7, 8 and 10 are a joy to use.

However, writing an installer that should be compatible with the majority of Windows systems (as many as possible) is not actually straight forward.  There are still things like credentials, roaming and non-roaming profiles, read/write rights, elevated users and functionality requiring admin notification (not to mention access bits on the filesystem itself); So a Windows box in Indonesia is not nessacarily identical to a Windows box in north America.

Microsoft have clear cut rules established for what paths to use when installing software. Smart Mobile Studio follows those rules always, but just because you follow the rules, doesnt mean that the user’s credentials (which propagate into everything he/she does) allows you to write all the files. This is where elevation comes in; Something that is finally easier to deal with in Delphi, the compiler we use to make Smart Mobile Studio.

We have seen people try to install Smart Mobile on thin clients and chrome-books, on roaming profiles in a corporate environment to their old Windows 98 machine. To make a long story short: modern Windows can be configured to be just about anything, and when installing a development platform you really need a normal PC without those restrictions. A developer machine.

Older installations

One of the biggest problems we have had and something that is topping our “how to” question chart, is when they have an ancient version of Smart Mobile Studio on their harddisk (anything from the first beta through version 2.0). This was before we added the automatic update program, and more importantly — the preferences files and dll files have since been utterly re-designed.

So they install the new version thinking it will replace some 3-4 year old install. But fact is, the installer will not overwrite the preferences file, and in many cases its not allowed to delete/replace the dll files. The old software prior to the update program must be manually un-installed through Windows before you install a more modern version (!)

What typically happens is that the old preferences file is left lingering on the system, the new executable tries to read it but finds none of the values it expects. This causes conflicts inside SMS, the server in particular is sensitive to this (which will be made more robust). This is not critical stuff, but annoying.

Using an old preferences file on a new exe will cause problems

Using an old preferences file on a new exe will cause problems, this is a typical stacktrace

The secondly, more critical, is when the older DLL files for the webkit rendering engine is left to linger. That means the header-files we use to talk with the DLL files wont match, causing a serious and show-stopping exception.

Again, manually uninstall the older version first. Then go into your “program files” folder and make sure to delete it completely. Same goes for “program data” (or appdata local or roaming). And last but not least, “user data” where library units, the RTL and various other tidbits are stored.

Using the update program

If you have a newer version you really dont need to download and use the installer. Simply start the update program and it will download the latest files. But its vital to remember that if you have made manual changes to the RTL files (which technically you shouldnt, but we dont mind as long as you stick to the license agreement for copying) – the update program will overwrite those files.

The point of the update program is to make sure the RTL, libraries, shims and IDE executable is the latest. It wont touch files that doesnt belong to SMS.

HTML5 Installer Object Pascal OP4JS Smart Mobile Studio

Events as objects

Posted on 18.01.2016 by Jon Lennart Posted in Developers log 5 Comments

Events are fun right? Well, only in part to be honest. For example, what do you do if you want to catch the same event but at different places?

This is where JavaScript’s addEventListener() comes into play. In short it allows you to add as many event-handlers to the same event as your heart desires. But raw unadulterated JavaScript is a bit of a mess, so I decided to wrap this up in clear cut objects. Oh, and I added a “fixed” event for when you want to have objects for standard events as well.

So now whenever you want to hook an event without ruining your published event-handlers (for instance in TW3CustomControl) you can just use one of these 🙂

Enjoy!

unit eventobjs;

interface

uses 
  w3c.dom,
  SmartCL.Components,
  SmartCL.System;

type


  TEventObjTriggeredEvent = procedure (sender:TObject;EventObj:JEvent);

  TEventObj = class(TObject)
  private
    FOwner:     TW3TagObj;
    FAttached:  Boolean;
    FEventName: String;
  protected
    procedure   HandleEvent(eobj:variant);virtual;
  public
    Property    Attached:Boolean read FAttached;
    procedure   Attach(EventName:String);
    procedure   Detach;
    constructor Create(AOwner:TW3TagObj);virtual;
    destructor  Destroy;Override;
  public
    Property    EventName:String read FEventName;
    Property    Owner:TW3TagObj read FOwner;
    Property    OnEvent: TEventObjTriggeredEvent;
  end;

  TFixedEventObj = class(TObject)
  protected
    FAttached:  Boolean;
    FOwner:     TW3TagObj;
    procedure   HandleEvent(eobj:variant);virtual;
  protected
    function    DoGetEventName:String;virtual;abstract;
  public
    Property    Attached:Boolean read FAttached;
    procedure   Attach;
    procedure   Detach;
    constructor Create(AOwner:TW3TagObj);virtual;
    destructor  Destroy;override;
  public
    Property    Owner:TW3TagObj read FOwner;
    Property    OnEvent: TEventObjTriggeredEvent;
  end;

  TElementRemovedEvent = class(TFixedEventObj)
  protected
    function  DoGetEventName:String;override;
  end;

  TElementAddedEvent = class(TFixedEventObj)
  protected
    function  DoGetEventName:String;override;
  end;

implementation


//#############################################################################
// TElementAddedEvent
//#############################################################################

function TElementAddedEvent.DoGetEventName:String;
begin
  result := "DOMNodeInserted";
end;

//#############################################################################
// TElementRemovedEvent
//#############################################################################

function TElementRemovedEvent.DoGetEventName:String;
begin
  result := "DOMNodeRemoved";
end;

//#############################################################################
// TFixedEventObj
//#############################################################################

constructor TFixedEventObj.Create(AOwner:TW3TagObj);
begin
  inherited Create;
  FOwner:=AOwner;
  Attach;
end;

destructor TFixedEventObj.Destroy;
begin
  Detach;
  inherited;
end;

procedure TFixedEventObj.Attach;
begin
  if FAttached then
  Detach;
  FOwner.Handle.addEventListener(DoGetEventName,@HandleEvent,true);
  FAttached := true;
end;

procedure TFixedEventObj.Detach;
begin
  if FAttached then
  begin
    FOwner.Handle.removeEventListener(DoGetEventName,@HandleEvent,true);
    FAttached := false;
  end;
end;

procedure TFixedEventObj.HandleEvent(eObj:variant);
begin
  if assigned(OnEvent) then
  OnEvent(self, JEvent(eObj));
end;

//#############################################################################
// TEventObj
//#############################################################################

constructor TEventObj.Create(AOwner:TW3TagObj);
begin
  inherited Create;
  FOwner := AOwner;
end;

destructor TEventObj.Destroy;
begin
  if FAttached then
  Detach;
  inherited;
end;

procedure TEventObj.HandleEvent(eobj:variant);
begin
  if assigned(OnEvent) then
  OnEvent(self,JEvent(eObj));
end;

procedure TEventObj.Attach(EventName:String);
begin
  if FAttached then
  Detach;

  FEventName := EventName;
  try
    FOwner.handle.addEventListener(FEventName,@HandleEvent,true);
  except
    FEventname:= '';
    FAttached:=false;
    exit;
  end;
  FAttached:=true;
end;

procedure TEventObj.Detach;
begin
  if FAttached then
  begin
    try
      FOwner.handle.removeEventListener(FEventName,@HandleEvent,true);
    finally
      FEventName := '';
      FAttached := false;
    end;
  end;
end;

end.
code events HTML5 javascript Object Pascal OP4JS Pascal Smart Mobile Studio w3C

Telling a 32bit float from a 64bit float

Posted on 16.01.2016 by Jon Lennart Posted in Developers log 2 Comments

The RTL is getting a bit of attention these days, with plenty of visual effects, tweens and more being added to it. It’s easy to forget that sometimes even the smallest things can be of utmost importance. Like how to tell a single from a double via code!

As you may have noticed, Smart Mobile suddenly got support for streams, memory allocation and the ability to manipulate data on byte and bit level. This may sound trivial but it actually changes how we write and deal with data completely. Under vanilla JavaScript working with bits and bytes can be a huge problem. Most JS developers shun native, raw data like the plauge – because they cant do much with it. Well, thats not going to be a problem for us! Buffers, streams, memory allocation, move, copy — easy as apple pie 🙂

Effect Virtual Machine

Amos Basic 2d and 3d

Amos Basic 2d and 3d

Today I was fiddling with a “mini language” module I have been working on. Actually it’s an effect module that allows you to script and compile effect logic in smart pascal itself, so almost like a second language. If you ever played around with Amos Basic on the Amiga home computer back in the 90’s, you may remember that Amos had a secondary animation language? Well, on that old computer sprites and animation was interrupt based (just think threads if you dont have any idea what hardware interrupts are). This allowed the creator of Amos Basic, Francois Lionet, to add a really cool animation language which was executed in the background — allowing your main program to run in parallell to your animation code.

I dont know how many months and years I spent coding Amos and BlitzBasic in my teens, but let’s just say it was A LOT! Francois Lionet and Mark Sibly, the author of BlitzBasic and the Blitz compiler chain were my absolute childhood heroes.

Well, that animation language (but better naturally) is in effect what I am hoping to achieve with my little virtual machine. I really want it to be super-easy to create awesome effects, and I want it to be fast, flicker free and smooth as a baby’s bottom. So a mini bytecode system seems like just the ticket to get that working – and naturally it’s written in smart itself, so you can play around with it when it’s done.

But back to the problem: I had to extend the TReader and TWriter class with the abillity to handle variant datatypes. This also meant adding “word” as a new RTL datatype to handle those 16 bit values. And last but not least — detecting the difference between 32 and 64 bit floating points.

I mean, if you get that wrong it’s going to cause havoc!

CodeSeg and DataSeg

In a bytecode virtual machine like, say, Java or CLR (common language runtime, .NET style) your compiler have to deal with two different things: code and data. One thing is variables (both global and local), but what about constants? Whenever you pass a fixed value to a procedure, that value is actually a constant. It doesnt change and it will be compiled with your program. Well this is where the compiler get’s smart and picks that up, and the value is stored in a special list. So when the program runs, it will fetch that constant from storage and use it. Or, in case of the CLR, it will be compiled directly into the bytecode if it’s an intrinsic value (long story).

Bytecode galore

Bytecode galore

As you probably guess, that’s called a dataseg (data segment), which is different from a codeseg, where the opcode and “asm” is stored.

So thats when i suddenly realized: we have no function in the RTL to tell the difference between a 32bit float and a 64bit float !

Well here is one way of telling the difference. It’s already incorporated into the Read/Write variant, which are functions I added to TReader and TWriter. So you dont have to worry about it. But for those that have (for some odd reason) been looking for this — here it is:

 function IsFloat32(const x:variant):Boolean;
 begin
  asm
  @result = isFinite(@x) && @x == Math.fround(@x);
  end;
 end;

Not much is it? But yet so important.

HTML5 javascript Object Pascal Smart Mobile Studio

Structures, dealing with name pairs

Posted on 26.09.2015 by Jon Lennart Posted in Developers log

When talking to your REST server or nodeJS service, it can sometimes be overkill to ship a huge and complex wad of JSON or XML over the wire. I noticed that WebSocket transferes can sometimes get a tiny delay if the server is dealing with high payloads. Although we are talking milliseconds here, it’s always good to refactor and make things as small as possible.

The solution I came up with is essentially a dynamically created “record”. Where you would normally write:

type
  TMyData = record
    field1: String;
    field2: TDateTime;
    field3: Integer;
  end;

TW3Structure allows you to do this programatically, both for binary and JSON. Which can be handy when playing around with your own protocols. So with TW3Structure you can do stuff like:

var Structure := TW3JsonStructure.Create(null);
Structure.WriteString('field1','some value');
Structure.WriteDateTime('field1',Now);
Structure.WriteInt('field2',1200);

var JSONData:String;
Structure.SaveToJSON(JSONData);

WebSocket.Send(JSONData);

I also added support for TStream which does the exact same thing, but in true binary form. Perfect for storing simple data in the browser cache or your mobile device’s sandbox.

unit system.namepair;

interface

uses
  System.types,
  System.streams,
  SmartCL.System;

type

  EW3Structure = class(EW3Exception);
  TW3Structure = Class(TObject)
  public
    procedure   WriteString(name:string;value:String;
                const encode:Boolean);overload;
    procedure   WriteInt(name:string;Value:Integer);overload;
    procedure   WriteBool(name:string;value:Boolean);overload;
    procedure   WriteFloat(name:String;value:Float);overload;
    procedure   WriteDateTime(name:String;value:TDateTime);overload;

    procedure   Write(name:String;value:variant);overload;virtual;abstract;

    procedure   Clear;virtual;abstract;

    procedure   SaveToJSON(var Data:String);virtual;abstract;
    procedure   LoadFromJSON(Value:String);virtual;abstract;

    procedure   SaveToStream(Stream:TStream);virtual;abstract;
    procedure   LoadFromStream(Stream:TStream);virtual;abstract;
  end;

  TW3JsonStructure = Class(TW3Structure)
  private
    FBuffer:  Variant;
  public
    procedure   Clear;override;
    procedure   SaveToJson(var Data:String);override;
    procedure   LoadFromJson(Value:String);override;
    procedure   SaveToStream(Stream:TStream);override;
    procedure   LoadFromStream(Stream:TStream);override;

    procedure   Write(name:String;value:variant);overload;override;
    Constructor Create(const Data:Variant);virtual;
  end;


  TW3BinProxyItem = Record
    daName:   String;
    daValue:  Variant;
  end;

  TW3BinaryStructure = Class(TW3Structure)
  private
    FData:      Array of TW3BinProxyItem;
    function    IndexOfName(name:String):Integer;
  protected
    procedure   Clear;override;
    procedure   SaveToJson(var Data:String);override;
    procedure   LoadFromJson(Value:String);override;
    procedure   SaveToStream(Stream:TStream);override;
    procedure   LoadFromStream(Stream:TStream);override;
    procedure   Write(name:String;value:variant);overload;override;
  end;


implementation

resourcestring

CNT_ERR_STRUCTURE_FUNCTION  =
'Stream storage failed, structure contains function reference error';

CNT_ERR_STRUCTURE_SYMBOL  =
'Stream storage failed, structure contains symbol reference error';

CNT_ERR_STRUCTURE_UNKNOWN =
'Stream storage failed, structure contains uknown data-type error';

CNT_ERR_STRUCTURE_FAILEDREAD =
'Failed to read structure, invalid datatype error';

CNT_ERR_STRUCTURE_INVALIDSIGN =
'Failed to read structure, invalid data signature error';

//############################################################################
// TW3BinaryStructure
//###########################################################################

const
CNT_STRUCT_SIGNATURE  = $CCCCFF00;

function TW3BinaryStructure.IndexOfName(name:String):Integer;
var
  x:  Integer;
begin
  result:=-1;
  if FData.Count>0 then
  for x:=FData.low to FData.high do
  begin
    if Sametext(Fdata[x].daName,Name) then
    begin
      result:=x;
      break;
    end;
  end;
end;

procedure TW3BinaryStructure.Write(name:String;value:variant);
var
  mIndex: Integer;
  mItem:  TW3BinProxyItem;
Begin
  mIndex:=IndexOfName(name);
  if mIndex<0 then
  begin
    mItem.daName:=name;
    mItem.daValue:=Value;
    FData.add(mItem);
  end else
  FData[mIndex].daValue:=Value;
end;

procedure TW3BinaryStructure.Clear;
Begin
  FData.Clear;
end;

procedure TW3BinaryStructure.SaveToJson(var Data:String);
Begin
  asm
    @Data = json.stringify( (@self.FData) );
  end;
end;

procedure TW3BinaryStructure.LoadFromJson(Value:String);
Begin
  asm
    (@self.FData) = json.parse(@Value);
  end;
end;

procedure TW3BinaryStructure.SaveToStream(Stream:TStream);
var
  x:  Integer;
  mWriter:  TWriter;
Begin
  mWriter:=TWriter.Create(Stream);
  try
    mWriter.WriteInteger(CNT_STRUCT_SIGNATURE);
    mWriter.WriteInteger(FData.Count);
    if FData.Count>0 then
    begin
      for x:=0 to FData.Count-1 do
      begin
        /* Write the name */
        mWriter.WriteString(FData[x].daName);

        /* Write the datatype */
        mWriter.WriteInteger(ord(FData[x].daValue.datatype));

        /* Write the value */
        case FData[x].daValue.datatype of
        vdBoolean:
          begin
            mWriter.WriteBoolean(FData[x].daValue);
          end;
        vdInteger:
          begin
            mWriter.WriteInteger(FData[x].daValue);
          end;
        vdFloat:
          begin
            mWriter.WriteDouble(FData[x].daValue);
          end;
        vdString:
          begin
            mWriter.WriteString(FData[x].daValue);
          end;
        vdFunction:
          begin
            Raise EW3Structure.Create(CNT_ERR_STRUCTURE_FUNCTION);
          end;
        vdSymbol:
          begin
            Raise EW3Structure.Create(CNT_ERR_STRUCTURE_SYMBOL);
          end;
        vdUnknown:
          begin
            Raise EW3Structure.Create(CNT_ERR_STRUCTURE_UNKNOWN);
          end;
        end;
      end;
    end;
  finally
    mWriter.free;
  end;
end;

procedure TW3BinaryStructure.LoadFromStream(Stream:TStream);
var
  mCount:   Integer;
  mReader:  TReader;
  mKind:    Integer;
  mRec:     TW3BinProxyItem;
Begin
  Clear;
  mReader:=TReader.Create(Stream);
  try
    if mReader.ReadInteger = CNT_STRUCT_SIGNATURE then
    begin
      mCount:=mReader.ReadInteger;
      while mCount>0 do
      begin
        mRec.daName:=mReader.ReadString;
        mKind:=mReader.ReadInteger;
        case TW3VariantDataType(mKind) of
        vdBoolean:  mRec.daValue:=mReader.ReadBoolean;
        vdInteger:  mRec.daValue:=mReader.ReadInteger;
        vdFloat:    mRec.daValue:=mReader.ReadDouble;
        vdString:   mRec.daValue:=mReader.ReadString;
        else
          Raise EW3Structure.Create(CNT_ERR_STRUCTURE_FAILEDREAD);
        end;

        FData.add(mRec);
        mRec.daName:="";
        mRec.daValue:=null;

        dec(mCount);
      end;
    end else
    Raise EW3Structure.Create(CNT_ERR_STRUCTURE_INVALIDSIGN);
  finally
    mReader.free;
  end;
end;

//############################################################################
// TW3Structure
//###########################################################################

procedure TW3Structure.WriteString(name:string;
          value:String;const encode:Boolean);
begin
  (* base64 encode string to avoid possible recursion
     should someone store another JSON object as a string *)
  if encode then
  Begin
    asm
      @value = btoa(@value);
    end;
  end;
  Write(name,value);
end;

procedure TW3Structure.WriteInt(name:string;Value:Integer);
begin
  Write(name,value);
end;

procedure TW3Structure.WriteBool(name:string;value:Boolean);
begin
  Write(name,value);
end;

procedure TW3Structure.WriteFloat(name:String;value:Float);
begin
  Write(name,value);
end;

procedure TW3Structure.WriteDateTime(name:String;value:TDateTime);
begin
  Write(name,value);
end;

//############################################################################
// TW3JsonStructure
//###########################################################################

Constructor TW3JsonStructure.Create(const Data:Variant);
begin
  inherited Create;
  if not TVariant.IsNull(Data)
  and not Data.IsUnassigned then
  FBuffer:=Data else
  FBuffer:=TVariant.CreateObject;
end;

procedure TW3JsonStructure.SaveToJSON(var Data:String);
begin
  if (FBuffer<>unassigned)
  and (FBuffer<>NULL) then
  Data:=JSON.Stringify(FBuffer) else
  Data:=null;
end;

procedure TW3JsonStructure.LoadFromJSON(Value:String);
begin
  value:=trim(Value);
  if value.length>0 then
  FBuffer:=JSON.Parse(value) else
  FBuffer:=TVariant.CreateObject;
end;

procedure TW3JsonStructure.SaveToStream(Stream:TStream);
var
  mWriter:  TWriter;
  mtext:    String;
begin
  SaveToJSON(mText);
  mWriter:=TWriter.Create(Stream);
  try
    mWriter.writeString(mText);
  finally
    mWriter.free;
  end;
end;

procedure TW3JsonStructure.LoadFromStream(Stream:TStream);
var
  mText:    String;
  mReader:  TReader;
begin
  mReader:=TReader.Create(stream);
  try
    mText:=mReader.ReadString;
  finally
    mReader.free;
  end;
  LoadFromJSON(mtext);
end;

procedure TW3JsonStructure.Clear;
begin
  FBuffer:=TVariant.CreateObject;
end;

procedure TW3JsonStructure.Write(name:String;value:variant);
begin
  FBuffer[name]:=value;
end;

end.
binary code JSON Records Streams Structures WebSocket

Giving back to the community

Posted on 04.06.2014 by Jon Lennart Posted in Announcements, News

For immediate release

Optimale Systemer AS is proud to announce the free version of our command-line compiler. This is the same compiler that powers Smart Mobile Studio; a toolchain which represents the most advanced object pascal to JavaScript compiler on the marked.

Continue reading→

command line compiler community compiler delphi free

Game programming – Part 2

Posted on 30.04.2013 by Jon Lennart Posted in Developers log, News

In the first installment of this article we had a look at how to load and display a tile based map. In this second post we will take it one step further by implementing scroll (movement) management – which is an essential part of our chosen genre of games.
Continue reading→

Game programming – Part 1

Posted on 25.04.2013 by Jon Lennart Posted in Developers log, News
Double Dragon on the Amiga 1989

Double Dragon on the Amiga 1989

When I was a kid I used to play tons of games on my Commodore 64 machine. This was the number one gaming machine of the early 80’s and was only surpassed by the Commodore Amiga in the late 80’s early 90’s in terms of popularity. The reason these machines were so popular was not just the games you could play on them, but also the fact that you could make your own games and applications on the same machine. They were not like the PlayStation or the xbox of our days – which turns brilliant children into customers only, with no means of playing with the device and exploring what it means to create.
Continue reading→

Graphics competition results

Posted on 09.04.2013 by Jon Lennart Posted in News

We are proud to present the winner of the Smart Mobile Studio graphics competition!

And the winner is .. Bioptopia webgl demo by Mattias Andersson – Congratulations!

Biotopia

Biotopia

You can testdrive the demo live by clicking here (Chrome is prefered)

In Mattias own words:

The demo consists of two parts — the first part is about drawing isosurfaces constructed by using a marching cubes implementation by Aaron Hochwimmer. The second part unites the isosurfaces with an extension of the CFDG language, which I call “3D-CFDG” (see http://www.contextfreeart.org for the original 2D implementation.) This was an idea that I had planned to implement at some point and this contest seemed like the perfect opportunity.

We congratulate Mattias with the #1 demo – job well done!

About the author

I kindly asked if the author could write a few words about himself, his background in demo coding and where he came from in terms of technology (being old Amiga hackers we naturally asked him if he ever did some coding on 16 bit computers). He was kind enough to reply with a short description. Again — hat off Mattias! Great work!

Lennart asked me to write a few lines about myself, so here it goes:

While I was certainly around in the old Amiga days (I’m born in 1981), I was a bit too young to actively engage in the “demo scene” myself. However I did help a friend of mine set up a BBS (we spent a lot of time working on fancy ANSI animations.) Those days you would download things by dialing up different BBSs with your 56k US Robotics modem; things such as music modules, demos and other cool stuff.

Some of the more prominent demo groups at this time were “future crew” and “triton” and they made some really spectacular demos. Triton was from Sweden (like me) and they wrote an amazing piece of software called FastTracker II, which was really way ahead of its time as far as software development goes (IMO.) I guess one thing I learned from this episode is that you don’t have to be a huge corporation in order to produce something great that a lot of people can benefit from. Ideas always originate with the individual and by crafting your ideas into something useful, it will benefit the whole of humanity (and quite possibly your own ego as well.) I guess this is one of the things that have motivated me to participate in various open source projects, such as Graphics32. I have also worked for a few years on a quite advanced image editing software that incorporates a node-based filter graph editor as well as a whole bunch of filter modules. I’m planning to continue this project after releasing my current Android project (a Z-brush-like software for Android that seemed like a more achievable short-term goal. P.S. Very good to now be able to test it on a tablet!)

Alright, now some words about my contest entry. The interesting part is clearly the CFDG implementation that I’ve managed to extend to three dimensions. CFDG is a simple language for describing geometric vector objects. There are some basic operations: scaling, translation, rotation and color adjustment. Additionally you know some predefined 3D “drawables”, such as a sphere, a cube or an extrusion along a lattice (you can add any primitive you like.) You define rules that will recursively invoke other rules. Recursion stops when the current scale parameter is smaller than a certain threshold value. Multiple versions of a rule can be implemented, whereby the effective rule will be randomly selected. This produces some very interesting geometric objects as can be seen by the end of the demo. One challenge in the implementation was how to move from one 3D reference space to another space transformed by a certain rotation. Fortunately I realized that quaternions would work ideally for this application. I encourage anyone interested in 3D graphics to learn about the benefits of quaternions.

Final verdict

Norway and Sweden have always been like two brothers. We like to tell jokes about each other, since we live right next to each other on the map I mean), have fun competitions from time to time (like the fact that we beat the crap out of your ice-hockey team last year) and we all meetup in the viking ship demo party once a year. Following Norwegian tradition we decided to deduct 10 points from the demo since you are in fact from Sweden — but we have to admit that even then you still won,  so fair is fair: great work Mattias! You won this compo lock, stock and barrel!

Creating a TScrollbox

Posted on 05.03.2013 by Jon Lennart Posted in News

The interface of the TW3Scrollbox:

unit w3scrollbox;
 
interface
 
uses w3system, w3components, w3graphics, w3scrollbar;
 
  //Default size of the scrollbars
  const
  CNT_SCROLLBAR_SIZE  = 18;
 
type
 
  TW3ScrollBoxContainer = Class(TW3CustomControl)
  End;
 
  TW3Scrollbox = Class(TW3CustomControl)
  Private
    FHScroll: TW3HorizontalScrollbar;
    FVScroll: TW3VerticalScrollbar;
    FContent: TW3ScrollBoxContainer;
    Procedure HandleScroll(sender:TObject);
    Procedure UpdateWhenReady;
  protected
    procedure ChildAdded(Const aChild:TW3Component);override;
    procedure InitializeObject; override;
    procedure FinalizeObject; override;
    procedure Resize; override;
  public
    Property  Content:TW3ScrollBoxContainer read FContent;
    Procedure Update;
  End;

Since all the child objects will be created runtime via the constructor in Smart, the JavaScript browser code might not be ready when ChildAdded() is called (which is invoked whenever a child control attach as a child). The “UpdateWhenReady” method will wait until both the DOM (document object model) and TApplication instance is truly ready, and then it will automatically update the scrollbars if there is any content that is larger than the client-area.

Take a look at the implementation:

implementation
 
 
  //###########################################################################
  // TW3Scrollbox
  //###########################################################################
 
  procedure TW3Scrollbox.InitializeObject;
  Begin
    inherited;
    FContent:=TW3ScrollBoxContainer.Create(self);
 
    FHScroll:=TW3HorizontalScrollbar.Create(self);
    FHScroll.OnChanged:=HandleScroll;
    FHScroll.visible:=False;
 
    FVScroll:=TW3VerticalScrollbar.create(self);
    FVScroll.OnChanged:=HandleScroll;
    FVScroll.visible:=False;
 
    //UpdateWhenReady;
  end;
 
  procedure TW3Scrollbox.FinalizeObject;
  Begin
    FHScroll.free;
    FVScroll.free;
    FContent.free;
    inherited;
  end;
 
  procedure TW3Scrollbox.ChildAdded(Const aChild:TW3Component);
  Begin
    inherited ChildAdded(aChild);
    UpdateWhenReady;
  end;
 
  Procedure TW3Scrollbox.UpdateWhenReady;
  begin
    if (w3_DOMReady=False)
    or (ObjectReady=False) then
    w3_callback(updateWhenReady,10) else
    w3_callback(Update,1);
  end;
 
  procedure TW3Scrollbox.Resize;
  var
    wd,hd:  Integer;
  begin
    inherited;
    wd:=clientWidth;
    hd:=clientHeight;
 
    if FHScroll.visible then
    dec(hd,CNT_SCROLLBAR_SIZE);
 
    if FVScroll.Visible then
    dec(wd,CNT_SCROLLBAR_SIZE);
 
    if FHScroll.Visible then
    FHScroll.setBounds(0,hd,wd,CNT_SCROLLBAR_SIZE);
 
    if FVScroll.Visible then
    FVScroll.setBounds(wd,0,CNT_SCROLLBAR_SIZE,hd);
 
    FContent.SetBounds(1,1,wd-2,hd-2);
  end;
 
  Procedure TW3Scrollbox.HandleScroll(sender:TObject);
  Begin
    FContent.ScrollInfo.ScrollTo(FHScroll.Position,FVScroll.Position);
  end;
 
  Procedure TW3Scrollbox.Update;
  begin
    BeginUpdate;
    try
      if FContent.ScrollInfo.ScrollWidth>FContent.ClientWidth then
      Begin
        FHScroll.Total:=FContent.ScrollInfo.ScrollWidth;
        FHScroll.PageSize:=FContent.ClientWidth;
        If not FHScroll.Visible then
        Begin
          FHScroll.Visible:=True;
          self.SetWasSized;
        end;
      end;
 
      if FContent.ScrollInfo.ScrollHeight>FContent.clientHeight then
      Begin
        FVScroll.Total:=FContent.ScrollInfo.ScrollHeight;
        FVScroll.PageSize:=FContent.clientHeight;
        If not FVScroll.Visible then
        Begin
          FVScroll.Visible:=True;
          SetWasSized;
        end;
      end;
 
      FContent.SendToBack;
 
    finally
      EndUpdate;
    end;
 
  end;
 
end.

Using the scrollbox (without the designer) is straight forward. In this case I added a Beatles poster as a resource, inserted an image into the content of the scrollbox – and used the image as a background (as opposed to the image itself. Neat trick!).

FBox:=TW3Scrollbox.Create(self);
FBox.SetBounds(10,200,600,500);
FBox.StyleClass:='TW3CustomControl';
 
FTest:=TW3Image.Create(FBox.Content);
FTest.Background.FromURL('res/beatles.jpg');
FTest.setBounds(0,0,1644 * 2, 1086 * 2);

Creating a scrollbar

Posted on 04.03.2013 by Jon Lennart Posted in Developers log, News

So, what is a scrollbar? In short it consists of 4 parts: an up arrow, a region to move a handle, the handle, and a down arrow. The hard part is not to draw or setup the actual control, but to translate between screen coordinates and the values the scrollbar represents. The scrollbar might represent 100000 items of “something”, but since you have (for instance) only 400 pixels to represent that sum – you have to translate between the visual and the abstract.
Continue reading→

Pages

  • About
  • Feature Matrix
  • Forums
  • News
  • Release History
  • Download
  • Showcases
    • The Smart Contest 2013, Round 1 – Graphics
  • Store
  • Documentation
    • Creating your own controls
    • Debugging, exceptions and error handling
    • Differences between Delphi and Smart
    • Get the book
    • Getting started
      • Introduction
      • Local storage, session storage and global storage
      • Application architecture
      • The application object
      • Forms and navigation
      • Message dialogs
      • pmSmart Box Model
      • Themes and styles
    • Layout manager
    • Networking
      • Loading files
      • TW3HttpRequest
      • TW3JSONP
    • Prerequisites
    • Real data, talking to sqLite
    • System requirements
    • Project types
      • Visual project
      • Game project
      • Console project

Archives

  • December 2019
  • December 2018
  • November 2018
  • July 2018
  • June 2018
  • February 2018
  • September 2017
  • April 2017
  • November 2016
  • October 2016
  • September 2016
  • April 2016
  • March 2016
  • January 2016
  • October 2015
  • September 2015
  • July 2015
  • April 2015
  • January 2015
  • December 2014
  • October 2014
  • September 2014
  • August 2014
  • July 2014
  • June 2014
  • March 2014
  • February 2014
  • January 2014
  • December 2013
  • November 2013
  • October 2013
  • August 2013
  • July 2013
  • June 2013
  • May 2013
  • April 2013
  • March 2013
  • February 2013
  • January 2013
  • December 2012
  • November 2012
  • August 2012
  • July 2012
  • June 2012
  • May 2012
  • April 2012
  • March 2012
  • February 2012
  • January 2012
  • November 2011
  • October 2011
  • September 2011

Categories

  • Announcements (25)
  • Developers log (119)
  • Documentation (26)
  • News (104)
  • News and articles (16)

WordPress

  • Register
  • Log in
  • WordPress

Subscribe

  • Entries (RSS)
  • Comments (RSS)
  • 1
  • 2
  • 3
  • …
  • 7
  • Next
© Optimale Systemer AS