Smart Desktop: Inter frame communication

April 24, 2017 Leave a comment

What is a process in a web desktop environment? Been thinking a bit about this, and it’s actually not that easy to hang the reality of an external script or website running in a frame as a solid “concept” (I use the word entity in my research notes). Visually it appears as a secondary, isolated web application – but ultimately (or technically) it’s just another web object with its own rendering context.

amibian_cortanaWhat I’m talking about is programs. Amibian.js or Smart desktop can already run a wealth of applications – which ultimately are just external or internal web-pages (a very simple term in some cases, but it will have to do). But when you want complex behavior from an eco-system, you must also provide an equally complex means of communication.

And this brings me neatly to the heart of todays task – namely how the desktop can talk to running web-applications and visa versa. And there are ultimately only two real options to choose from here: pure client, or client-server.

Unlike other embedded / web desktops I want to do as much as possible client-side. It would take me five minutes to implement message dispatching if I did this server-side, but the less dependent we are on a server – the more flexible amibian.js will be in terms of integration.

Screenshot

The first Desktop-Application type running in the desktop! And its aware that it is running under Amibian. This frame/host awareness helps establish parent/child relationship

Turns out that the browser has an API for this. A message API that allows a main website to talk with other websites embedded in IFrames. Which is just what we need.

But its very crude and not as straight forward as you may think. You have commands like postMessage() and then you can set up an event-handler that fires whenever something comes through. But you also have security measures to think about. What if you interface with a web-application designed to kill the system? Without the safety of an abstraction layer that would indeed be a very easy hack.

The Smart message API

This has been in the RTL for quite some time but I havent really used it for much. I had one demo a couple of years back that used the message-api to handle off-screen rendering in a separate IFrame (sort of poor mans thread), but that was it.

Well, time to update and bring it into 2017! So I give you the new and improved inter-frame, inter-window and inter-domain message framework! (ta da!)

It should be self-evident how to use it, but a little context wont hurt:

  • You inherit from TW3MessageData and implement whatever data-fields you need
  • You send messages using the w3_SendMessage() method
  • You broadcast (i.e send the message to all frames and windows listening) messages using the w3_broadcastMessage method

But here comes the twist, because I hate having to write a bunch of if/then/else switches for messages. So instead I created a simple subscription system! Super small yet very efficient!

msgbroker

In other words, you inform the system which messages you support, which then becomes your subscription. Whenever the local dispatcher encounters that particular message type  – it will forward the message object to you.

Here is the code. Notice the helper functions that deals with finding the current domain and so on – this will be handy when establishing a parent/child relationship.

Also note: you are expected to inherit from these classes and shape them to your message types. I have implemented the bare-bones that mimic’s Windows message API. And yes, please google inter-frame communication while testing this.

unit SmartCL.Messages;

interface

uses
  w3c.dom,
  System.Types,
  System.Time,
  System.JSON,
  Smartcl.Time,
  SmartCL.System;

const
  CNT_QTX_MESSAGES_BASEID = 1000;

type

  TW3MessageData         = class;
  TW3CustomMsgPort       = class;
  TW3OwnedMsgPort        = class;
  TW3MsgPort             = class;
  TW3MainMessagePort     = class;
  TW3MessageSubscription = class;

  TW3MessageSubCallback  = procedure (Message: TW3MessageData);
  TW3MsgPortMessageEvent = procedure (Sender: TObject; EventObj: JMessageEvent);
  TW3MsgPortErrorEvent   = procedure (Sender: TObject; EventObj: JDOMError);

  (* The TW3MessageData represents the actual message-data which is sent
     internally by the system. Notice that it inherits from JObject, which
     means it supports 1:1 mapping from JSON.
     This is why Deserialize is a member, and Serialize is a class member. *)
  TW3MessageData = class(JObject)
  public
    property    ID: Integer;
    property    Source: String;
    property    Data: String;

    function Deserialize: string;
    class function Serialize(const ObjText: string): TW3MessageData;

    constructor Create;
  end;

  (* A "mesage port" is a wrapper around the [obj]->Window[]->OnMessage
     event and [obj]->Window[]->PostMessage API.
     Where [obj] can be either the main window, or an embedded
     IFrame->contentWindow. This base-class implements the generic behavior.
     You must inherit or use a decendant class if you dont know exactly what
     you are doing! *)
  TW3CustomMsgPort = Class(TObject)
  private
    FWindow:    THandle;
  protected
    procedure   Releasewindow;virtual;
    procedure   HandleMessageReceived(eventObj: JMessageEvent);
    procedure   HandleError(eventObj: JDOMError);
  public
    Property    Handle:THandle read FWindow;
    Procedure   PostMessage(msg:Variant;targetOrigin:String);virtual;
    procedure   BroadcastMessage(msg:Variant;targetOrigin:String);virtual;
    Constructor Create(WND: THandle);virtual;
    Destructor  Destroy;Override;
  published
    property    OnMessageReceived: TW3MsgPortMessageEvent;
    Property    OnError: TW3MsgPortErrorEvent;
  end;

  (* This is a baseclass for window-handles that already exist.
     You are expected to provide that handle via the constructor *)
  TW3OwnedMsgPort = Class(TW3CustomMsgPort)
  end;

  (* This message-port type is very different from both the
     ad-hoc baseclass and "owned" variation above.
     This one actually creates it's own IFrame instance, which
     means it's a completely stand-alone entity which doesnt need an
     existing window to dispatch and handle messages *)
  TW3MsgPort = Class(TW3CustomMsgPort)
  private
    FFrame:     THandle;
    function    AllocIFrame: THandle;
    procedure   ReleaseIFrame(aHandle: THandle);
  protected
    procedure   ReleaseWindow; override;
  public
    constructor Create; reintroduce; virtual;
  end;

  (* This message port represent the "main" message port for any
     application that includes this unit. It will connect to the main
     window (deriving from the "owned" base class) and has a custom
     message-handler which dispatches messages to any subscribers *)
  TW3MainMessagePort = Class(TW3OwnedMsgPort)
  protected
    procedure HandleMessage(Sender: TObject; EventObj: JMessageEvent);
  public
    constructor Create(WND: THandle); override;
  end;

  (* Information about registered subscriptions *)
  TW3SubscriptionInfo = Record
    MSGID:    integer;
    Callback: TW3MessageSubCallback;
  end;

  (* A message subscription is an object that allows you to install
     X number of event-handlers for messages you want to recieve. Its
     important to note that all subscribers to a message will get the
     same message -- there is no blocking or ownership concepts
     involved. This system is a huge improvement over the older WinAPI *)
  TW3MessageSubscription = class(TObject)
  private
    FObjects:   Array of TW3SubscriptionInfo;
  public
    function    ValidateMessageSource(FromURL: string): boolean; virtual;
    function    SubscribesToMessage(const MSGID: integer): boolean; virtual;
    procedure   Dispatch(const Message: TW3MessageData); virtual;
    function    Subscribe(const MSGID: integer; const CB: TW3MessageSubCallback): THandle;
    procedure   Unsubscribe(const Handle: THandle);

    Constructor Create; virtual;
    Destructor  Destroy; override;
  end;

  (* Helper functions which simplify message handling *)
  //function  W3_MakeMsgData:TW3MessageData;
  procedure W3_PostMessage(const msgValue: TW3MessageData);
  procedure W3_BroadcastMessage(const msgValue: TW3MessageData);

  (* Audience returns true if a message-ID have any
     subscriptions assigned to it *)
  function  W3_Audience(msgId: integer): boolean;

  (* This returns true if the current smart application is embedded in a frame.
     That can be handy to know when establishing parent/child relationships
     between main-application and child "programs" running in frames *)
  function AppRunningInFrame: boolean;
  function GetUrlLocation: string;
  function GetDomainFromUrl(URL: string; const IncludeSubDomain: boolean): string;

implementation

uses SmartCL.System;

var
_mainport:    TW3MainMessagePort = NIL;
_subscribers: Array of TW3MessageSubscription;

function AppRunningInFrame: boolean;
var
  LMyFrame: THandle;
begin
  // window.frameElement Returns the element (such as <iframe> or <object>)
  // in which the window is embedded, or null if the window is top-level.
  try
    // Note: attempting to read frameElement will throw a SecurityError
    // exception in cross-origin iframes. It should just return null,
    // but not all browsers play by the rules -- hence the try/catch
    asm
      @LMyFrame = window.frameElement;
    end;
  except
    on e: exception do
    exit;
  end;
  result := not (LMyFrame);
end;

function GetUrlLocation: string;
begin
  asm
    @result = window.location.hostname;
  end;
end;

function GetDomainFromUrl(Url: string; const IncludeSubDomain: boolean): string;
begin
  url := Url.trim();
  if url.length < 1 then
  begin
    asm
      @Url = window.location.hostname;
    end;
  end;
  asm
    @url = (@url).replace("/(https?:\/\/)?(www.)?/i", '');
    if (!@IncludeSubDomain) {
        @url = (@url).split('.');
        @url = (@url).slice((@url).length - 2).join('.');
    }
    if ((@url).indexOf('/') !== -1) {
      @result = (@url).split('/')[0];
    } else {
      @result = @url;
    }
  end;
end;

//#############################################################################
//
//#############################################################################

procedure QTXDefaultMessageHandler(sender:TObject;EventObj:JMessageEvent);
var
  x:      Integer;
  mItem:  TW3MessageSubscription;
  mData:  TW3MessageData;
begin
  mData := TW3MessageData.Serialize(EventObj.Data);
  //mData := new TW3MessageData();
  //mData.Serialize(EventObj.Data);

  for x:=0 to _subscribers.count-1 do
  Begin
    mItem := _subscribers[x];

    // Check that the message is subscribed to
    if mItem.SubscribesToMessage(mData.ID) then
    begin
      // Make sure subscriber validates the caller-domain
      if mItem.ValidateMessageSource( TVariant.AsString(EventObj.source) ) then
      begin
        (* We execute with a minor delay, allowing the browser to
         exit the function before we dispatch our data *)
        TW3Dispatch.Execute(procedure ()
        begin
          mItem.Dispatch(mData);
        end, 10);
      end;
    end;
  end;
end;

{ function W3_MakeMsgData: TW3MessageData;
begin
  result := new TW3MessageData();
  result.Source := "*";
end;      }

function W3_GetMsgPort: TW3MainMessagePort;
begin
  if _mainport = NIL then
  _mainport := TW3MainMessagePort.Create(BrowserAPI.Window);
  result := _mainport;
end;

procedure W3_PostMessage(const msgValue:TW3MessageData);
begin
  if msgValue<>NIL then
  W3_GetMsgPort().PostMessage(msgValue.Deserialize,msgValue.Source) else
  raise exception.create('Postmessage failed, message object was NIL error');
end;

procedure W3_BroadcastMessage(const msgValue:TW3MessageData);
Begin
  if msgValue<>NIL then
  W3_GetMsgPort().BroadcastMessage(msgValue,msgValue.Source) else
  raise exception.create('Broadcastmessage failed, message object was NIL error');
end;

function  W3_Audience(msgId: Integer): boolean;
var
  x:  Integer;
  mItem:  TW3MessageSubscription;
begin
  result:=False;
  for x:=0 to _subscribers.count-1 do
  Begin
    mItem:=_subscribers[x];
    result:=mItem.SubscribesToMessage(msgId);
    if result then
    break;
  end;
end;

//#############################################################################
// TW3MainMessagePort
//#############################################################################

Constructor TW3MessageData.Create;
begin
  self.Source:="*";
end;

function  TW3MessageData.Deserialize: string;
begin
  result := JSON.Stringify(self);
end;

class function TW3MessageData.Serialize(const ObjText: string): TW3MessageData;
begin
  result := TW3MessageData(JSON.parse(ObjText));
end;

//#############################################################################
// TW3MainMessagePort
//#############################################################################

constructor TW3MainMessagePort.Create(WND: THandle);
begin
  inherited Create(WND);
  OnMessageReceived := QTXDefaultMessageHandler;
end;

procedure TW3MainMessagePort.HandleMessage(Sender: TObject; EventObj: JMessageEvent);
begin
  QTXDefaultMessageHandler(self, eventObj);
end;

//#############################################################################
// TW3MessageSubscription
//#############################################################################

Constructor TW3MessageSubscription.Create;
begin
  inherited Create;
  _subscribers.add(self);
end;

Destructor TW3MessageSubscription.Destroy;
Begin
  _subscribers.Remove(self);
  inherited;
end;

function TW3MessageSubscription.Subscribe(const MSGID: integer; const CB: TW3MessageSubCallback): THandle;
var
  LObj: TW3SubscriptionInfo;
begin
  LObj.MSGID := MSGID;
  LObj.Callback := @CB;
  FObjects.add(LObj);
  result := LObj;
end;

procedure TW3MessageSubscription.Unsubscribe(const Handle: THandle);
var
  x:  Integer;
begin
  for x:=0 to FObjects.Count-1 do
  begin
    if variant(FObjects[x]) = Handle then
    Begin
      FObjects.delete(x,1);
      break;
    end;
  end;
end;

function TW3MessageSubscription.ValidateMessageSource(FromURL: string): boolean;
begin
  // by default we accept messages from anywhere
  result := true;
end;

function TW3MessageSubscription.SubscribesToMessage(const MSGID: integer): boolean;
var
  x:  Integer;
begin
  result:=False;
  for x:=0 to FObjects.Count-1 do
  Begin
    if FObjects[x].MSGID = MSGID then
    Begin
      result:=true;
      break;
    end;
  end;
end;

procedure TW3MessageSubscription.Dispatch(const Message:TW3MessageData);
var
  x:  Integer;
begin
  for x:=0 to FObjects.Count-1 do
  Begin
    if FObjects[x].MSGID = Message.ID then
    Begin
      if assigned(FObjects[x].Callback) then
      FObjects[x].Callback(Message);
      break;
    end;
  end;
end;

//#############################################################################
// TW3MsgPort
//#############################################################################

Constructor TW3MsgPort.Create;
begin
  FFrame:=allocIFrame;
  if (FFrame) then
  inherited Create(FFrame.contentWindow) else
  Raise Exception.Create('Failed to create message-port error');
end;

procedure TW3MsgPort.ReleaseWindow;
Begin
  ReleaseIFrame(FFrame);
  FFrame:=unassigned;
  Inherited;
end;

Procedure TW3MsgPort.ReleaseIFrame(aHandle:THandle);
begin
  If (aHandle) then
  Begin
    asm
      document.body.removeChild(@aHandle);
    end;
  end;
end;

function TW3MsgPort.AllocIFrame: THandle;
Begin
  asm
    @result = document.createElement('iframe');
  end;

  if (result) then
  begin
    /* if no style property is created, we provide that */
    if not (result['style']) then
    result['style'] := TVariant.createObject;

    /* Set visible style to hidden */
    result['style'].display := 'none';

    asm
      document.body.appendChild(@result);
    end;

  end;
end;

//#############################################################################
// TW3CustomMsgPort
//#############################################################################

Constructor TW3CustomMsgPort.Create(WND: THandle);
Begin
  inherited Create;
  FWindow := WND;
  if (FWindow) then
  begin
    FWindow.addEventListener('message', @HandleMessageReceived, false);
    FWindow.addEventListener('error', @HandleError, false);
  end;
End;

Destructor TW3CustomMsgPort.Destroy;
begin
  if (FWindow) then
  begin
    FWindow.removeEventListener('message', @HandleMessageReceived, false);
    FWindow.removeEventListener('error', @HandleError, false);
    ReleaseWindow;
  end;
  inherited;
end;

procedure TW3CustomMsgPort.HandleMessageReceived(eventObj:JMessageEvent);
Begin
  if assigned(OnMessageReceived) then
  OnMessageReceived(self,eventObj);
End;

procedure TW3CustomMsgPort.HandleError(eventObj:JDOMError);
Begin
  if assigned(OnError) then
  OnError(self,eventObj);
end;

procedure TW3CustomMsgPort.Releasewindow;
begin
  FWindow := Unassigned;
end;

Procedure TW3CustomMsgPort.PostMessage(msg:Variant;targetOrigin:String);
begin
  if (FWindow) then
  FWindow.postMessage(msg,targetOrigin);
end;

// This will send a message to all frames
procedure TW3CustomMsgPort.BroadcastMessage(msg:Variant;targetOrigin:String);
var
  x:  Integer;
  mLen: Integer;
begin
  mLen := TVariant.AsInteger(browserAPI.Window.frames.length);
  for x:=0 to mLen-1 do
  begin
    browserAPI.window.frames[x].postMessage(msg,targetOrigin);
  end;
end;

finalization
begin
  if assigned(_mainport) then
  _mainport.free;
end;

end.

Amibian.js and the Narcissus hack

April 22, 2017 Leave a comment

Wow, I must admit that I never really thought Amibian.js would become even remotely as popular as it has – yet people respond with incredible enthusiasm to our endeavour. I was just told that an article at Commodore USA mentioned us – that an exposure to 37000 readers. Add that to the roughly 40.000 people that subscribe to my feeds around the world and I must say: I hope I code something worthy of your time!

amibian_cortana

This is going to look and behave like the bomb when im done!

But there is a lot of stuff on the list before it’s even remotely finished. This is due to the fact that im not just juggling one codebase here – im juggling 5 separate yet interconnected codebases at the same time (!). First there is the Smart Mobile Studio RTL (run time library) which represents the foundation. This gives me object-oriented, fully inheritance driven visual controls. This have roughly 5 years of work behind it.

138-Quake_III_Arena-1479915663

Still #1 after all these years

On top of that you have the actual visual controls, like buttons, scrollbars, lists, css3 effect engines, tweening, database storage and a ton of low-level stuff. The browser have no idea what a window is for example, let alone how it should look or respond to users. So every little piece has to be coded by someone. And well, that’s what I do.

Next you have the workbench and operating-system itself. What you know as Amibian.js, Smart Workbench or Quartex media desktop – take your pick, but it’s already a substantial codebase spanning some 40 units with thousands of lines of code. It is divided into two parts: the web front-end that you have all seen; and the node.js backend that is not yet made public.

And on top of that you have the external stuff. Quake III didn’t spontaneously self-assemble inside the desktop, someone had to do some coding and make the two interface. Same with all the other features you have.

The worst so far (as in damn hard to get right) is the Ace text editor. Ace by itself is super easy to work with – but you may have noticed that we have removed it’s scrollbars and replaced these with Amiga scrollbars instead? That is a formidable challenge it its own right.

patch2

Aced it! Kidnapping signals was a challenge

Whats on the menu this weekend?

I noticed that on Linux that text-selection was utterly messed up, so when you moved a window around – it would suddenly start selecting the title text of other elements around the desktop. This is actually a bug in the browser – not my code; but I still have to code around it. Which I have now done.

I also solved selection for the console window (or any “text” container. A window is made up of many parts and the content region can be inherited from and replaced), so that should now work fine regardless of browser and platforms. Ace theming also works, and the vertical scrollbar is responding as expected. Still need a few tweaks to move right, but that is easy stuff. The hard part is behind us thankfully.

patch

Selection – works

Right now I’m working on ScummVM so that should be in place later today 🙂

Thats cool, but what motivates you?

2jd3x2q

Cult of Joy

Retro gaming is important, and we have to make it as easy for people to enjoy their retro gear without patent trolls ruining the fun. Im just so tired of how ruined the Amiga scene is by these (3 companies in particular).. thieves is the only word I can find that fits.

So fine! I will make my own. Come hell or high water. Free as a bird and untouchable.

So I have made some tools that will make it ridiculously easy for you to share, download and play your games online. Whenever you want, hosted where-ever you need and there is not a god damn thing people can do about it. When you realize how simple the hack is it will make you laugh. I came up with this ages ago and dubbed it Narcissus.

To understand the Narcissus hack, consider the following:

PNG is a lossless compression format, meaning that it doesn’t lose any information when compressed. It’s not like JPEG which scrambles the original and saves a faximile that tricks the eye. Nope, if you compress a PNG image you get the exact same out when you decode it (read: show it).

But who said we have to store pixels? Pixels are just bytes after all. In fact, why can’t we take a whole game disk or rom and store that inside a picture? Sure you can!

cool

This tool is now built into Amibian.js

It’s amusing, I came up with this hack years ago. It has been a part of Smart Mobile Studio since the beginning.

You have to remember that retro games are super small compared to modern games. The average ADF file is what? 880kb or something like that? Well hold on to your hat buddy, because PNG can hold 64 megabyte of data! You can encode a decent Amiga hard disk image in 64 megabyte.

eatmeCan you guess what the picture on the right contains? This picture is actually ALL the Amiga rom files packed into a single image. Dont worry, I converted it to JPEG to mess up the data before uploading. But yes, you can now host not just the games as normal picture files, but also roms and whatever you like.

And the beauty of it – who the hell is going to find them? You can host them on Github, Google drive, Dropbox or right your blog — if you don’t have the encryption key the file is useless.

Snap, crackle and pop!

RSS Filesystem

You know RSS feeds right? If you sign up for a blog you automatically get a RSS feed. It’s basically just a list of your recent posts – perhaps with an extract from each article, a thumbnail picture and links to each post. RSS have been around for a decade or more. It’s a great way to keep track of news.

The second hack is that using the data-to-image-encoder you can store a whole read-only filesystem as a normal RSS feed. Always think outside the box!

Let’s say you have a game collection for your Amiga right? Lets say 200 games. Wouldnt it be nice to have all those games online? Just readily available regardless of where you may be? Without “you know who” sending you a nasty email?

Well, just encode your game as described above, include the data-picture in your WordPress post, and do that for each of your games. Since you can encrypt these images they will be worthless to others. But for you its a neat way of hosting all your games online for free (like WordPress or Blogger) and play them via Amibian or the patched UAE4Arm (ops, did I share that, sorry dirk *grin*) and you’re home free.

You know what’s really cool? For this part Amibian doesnt even need a server. So you can just save the Amibian.js html page on your phone and that’s all you need.

RumoredPirates

Drink up me hearties yo ho!

Smart Puppy: Smart pascal meets linux!

April 21, 2017 Leave a comment

logo_waifu2x_art_noise1_scale_tta_1One of my absolute favorite operating-systems in the whole world has to be Puppy Linux. I discovered it just a few days ago and I have fallen completely in love with this thing. I can vaguely remember giving it a testdrive a few years back, but I didn’t know much about Linux in general so I didn’t understand what I it represented.

So if you are looking for a friendly, small, fast and easy to use Linux system – then Puppy is about as friendly as it gets. The Facebook user group with the same name is a warm and friendly place to be. Much like Delphi developer the Admin(s) take pride in keeping things orderly – and people who hang out there engage, care and help each other out.

Before you run out and download Puppy, which I hope you do later – please understand that Puppy is very different from Linux in general. You could almost say that it’s a whole alternative to mainstream Linux as we know it.

But, once you know about the differences then you are in for a treat! I will explain them in the article, so please be patient and take the time to digest.

Puppies hate fluff

One of the reasons I never converted wholesale to Linux (and yes I did try) – is that the average Linux distro is unbearable and unnecessary cryptic. For some reason Linux architects suffer from a terrible affliction, namely a shortage of characters. This sickness means that Linux don’t have enough characters for everyone, so programmers must use a maximum of five letters when naming their software. If coders ignore this shortage and blatantly name something directly or intuitively – then Richard Stallman and Lady Gaga will order a “drive-by pony tail cut” on the dude. And a Linux administrator without his pont-tail is finished (the nerd equivalent of flipping burgers at McDonalds).

Puppy Linux does contain it’s fair share of the classical Linux software (that goes without saying). But, the man behind this wonderful Linux flavour is also a level-headed, clever and resourceful man (or woman) – so he has thankfully broken with what can only be described as archaic thinking.

puppy01

Puppy Linux is not exactly software impaired

So even with my minimal Linux experience I was able to navigate around the filesystem, locate documents (which here is called “Documents” and “My Documents” even). There is a whole bunch of these tiny differences, small things that makes all the difference. From the way he (or she) has named things – to where things are stored and placed.

And it’s so small! The basic install is less than 300 megabytes in size (!) Yes you read that right. The generic Puppy Linux installation with desktop and a few popular applications is less than 300 megabytes.

In my case I can have a fully loaded development studio, featuring GCC, FPC (freepascal), Lazarus IDE, CodeBlocks IDE, KDevelop IDE, Anjunta developer studio – and last but never least Smart Mobile Studio on a 2 gigabyte USB stick (!) I don’t think you can even get USB sticks that small any more (?) The smallest I got is 32 gigabyte and the largest is 256 gigabyte.

But before we go on with the wonders of Puppy Linux – lets look at what Linux did wrong. Why is Linux even to this day considered hard to use? Or to put it another way: what has Windows and OS X done right to be considered easier to use yet capable of the same (and often more) ?

Naming, what Linux did wrong

One of the tenants of professional programming, is to ensure that classes, members and functions have meaningful names. There was a time when you would get away with single character class, variable and method names — but that wont fly in 2017. Your Q&A department would have you for breakfast if you checked in code like that. Classes, name-spacing and packages should be descriptive. End of story.

The reason this has become an almost sacred law, should be obvious: it may not be you that maintains the software 5, 10 or 15 years down the road. A piece of code should always be written in such a way that it can be understood and thus maintained by others within a reasonable time-frame (which also means plenty of comments and good documentation). This is not a matter of preference, but of time and money. And when you pay out salaries these factors are one and the same.

So naming elements of software in 2017 has a lot of criteria attached to it. The most obvious so far being:

  • Always name things clearly because that
    • ensures ease of use
    • simplifies maintenance
    • removes doubt as to “what is what”
    • less user-mistakes
  • The less mistakes, either in understanding something or using something, the less money a business throws out the window. Money that could be spent paying you to make something cool instead (or fix bugs that are critical).
  • The less user-mistakes caused by customers, the more your service department can focus on quality of service. When a company starts it usually have outstanding support, but as it grows their service-desk slowly become robots.
  • The easier and more intuitive a system is, the more users it will attract. If people can pick something up and just naturally figure out how things work, then statistics show that they most likely will continue using it through thick and thin.

Right. With these rules in mind – what happens if you take them but apply them to Linux instead? Not Linux code or libraries or stuff like that, but Linux the user-experience from top to bottom?

And don’t get me wrong, I think Linux is awesome so this is not an attack on Linux; I’m simply pointing out factors that could help make Linux even better.

I mean, just look at the Linux filesystem. Again you have this absurd shortage of characters. Why would anyone abbreviate the word “user[s]” into “usr” ? It make noh sense.  Same with “lib”, would it have killed you to call it “libraries”? And so it continues with “dev” – because calling it “devices” would cause the space-time-continuum to break.

Shell shocked

The shell (or command-line under Windows) and it’s commands is really the thing that annoys me the most. There is a fine line between use and abuse, and the level of abbreviation here is beyond whimsical and harmless – and well into the realm of silly and absurd

Who in their right mind would name a command “ps”? What could it possibly mean? The first thing that comes to mind is “print spool”. If you come from any other platform than Linux (and perhaps Unix, I don’t know) you would never imagine that “ps” actually means “list all running processes and their states”.

ps_command

“ps” lists the running processes and their states

Above: running “ps” from the shell lists the running processes. Would it have killed the coders to just call it, oh perhaps, “listprocesses” or “showrunningprograms”?

The “ps” command is just one in a long, long list of commands that really should be brought into the twenty-first century. The benefits should be obvious. It should not be necessary for a 43-year-old man to blog about this, because it’s been a problem for the better part of three decades.

  • Kids and teenagers is the bread and butter for all operating systems. The faster a kid of teenager can do something with a system, the more loyal that individual will be to the platform in the future.
  • Linux needs developers and users from other platforms. When someone who has been a successful developer for almost 30 years find a system cryptic and hard to use, how much harder will it be for a non-technical user?
  • Standards are important. The location of files, libraries and settings should be uniform. As of writing Linux seem to have 3 different standards (again, I am no expert): systemd, initd and “systemx”. The latter is just a name I made up, because no-one really knows what it’s called. We are now in the realm of PlayStation, ChromeOS, WebOS and systems that build on the Linux – but deviate the moment the drivers have loaded.

Again, I’m not writing this in a negative mindset. I have been using Ubuntu for a while as an alternative to Windows and OS X. But this has been a purely user-centric experience. I have not done any programming except random bits of Freepascal and node.js experiements. I have enjoyed Ubuntu purely as a user. Writing documents, checking email, browsing the web, IRC, reading news groups – ordinary stuff.

So I am very positive to Linux, but I have yet to find “my” flavour of it. A Linux distro I feel at home with and that appeals to my way of working.

Until today that is..

Enter puppy Linux

Puppy is a flavour of Linux that just demolishes some of Linux’s most holiest of concepts. Everyone will tell you never to run as root, always have the root account in peace – and keep it under lock and key just in case someone gets into your second account right?

Well not Puppy. Here you are expected to run as root and you can, if you for some reason must, jump out into a secondary user which is fake. So indeed – puppy Linux is a single user Linux system. It’s the rebel, the scoundrel and rouge of the Linux world – the distro that couldn’t care less what the other guys are doing.

gcc.png

Fancy a spot of coding? GCC is a SFS module away ..

Secondly, and this is very cool, Puppy is highly modular. No I’m not talking about packages, all Linux distros have that in some form or another. No I’m talking about something called SFS files, short for squashed file-system.

To make a long story short, Puppy allows you to mount compressed files as disks and they become a part of the system. It’s a bit like the virtual-drive API on windows (if you have ever coded against that?). You may have noticed in Windows how you can double-click on a .ISO file and suddenly the file is mounted in the file-explorer and stays mounted until you manually dis-mount the damn thing?

Well, SFS is that but also much more. Because when you mount the SFS file whatever applications it contains registers on the start-menu, adds itself to the global path and essentially becomes one with the whole system. This took me a while to wrap my head around this (good features always comes with a price, so i keept waiting for the negative. But there were none!). The people I talked to about this were not coders, so they had some very colorful explanations to how it all worked. But once I realized SFS was just a zip-file (or tarball or whatever) with a fixed structure (including mount script and dis-mount script) I got the picture.

Size and speed matters

Before I started using a PC back in the early 90’s I was a huge Amiga fan. I still am (as you no doubt have noticed). One of the first things I found, or first difference between Amiga computing and PC computing that hit me – was how wasteful PC’s were. I remember I was shocked when I saw how much space and cpu power the average programmer just wasted — because on the Amiga everyone strived to be as resourceful and efficient as possible.

We would spend days optimizing even the smallest parts of our applications just to ensure that it ran at top speed and produced as little bloat as possible. This was just baked into us, it was the way of the force and as common as your grandfather’s work ethics. Quality and achievement went hand in hand.

cb

CodeBlocks is an excellent IDE 🙂

When you fire up Puppy Linux you are instantly reminded that there are people to this day that cares about size and speed. And that maybe, just maybe, consumerism has tricked you into throwing away perfectly usable technology year after year. Machines that actually had more than enough CPU power for the tasks you wanted, but was slowed down by bloated operating-systems, poor programming and lazy code generators.

Puppy Linux is the fastest bloody Linux you will ever run. The only operatingsystem I have tried that runs faster, is Aros compiled for Arm (a distro called Aeros, a reverse engineered edition of Amiga OS). But as far as x86 and the Linux kernel goes — Puppy Linux is the bomb.

I know I’m repeating myself here but: less than 300 megabytes for a fully loaded Linux distro with text processor, browser, devkit, music player, video player and all the “typical” applications you would use for daily tasks? And it truly is the fastest hunk of junk in the galaxy without question.

Amiga coders and the cult of joy

When I started to snoop around the Puppy environment and community, I started to notice a couple of “tell-tell” signs. Tiny, subtle things that only an Amiga coder would pick up on. Enough to give you a hunch, a gut feeling – but not enough to blatantly say it out loud. “Amiga guys did this” i would whisper to myself. And it’s not really such a big surprise to find that coders now in their 40s that used to be Amiga coders.

In 30 years time there will be company owners and CEO’s that grew up with Playstation and have fond memories of that. But they wont recognize each-other by their craftmanship – that is the difference.

cult

The cult of joy lives on, albeit in new forms

The Amiga was special because it was not just a games machine. It was also a complete rewrite of what constituted the power operatingsystem of its time: Unix. In other words they copied the best stuff from Unix (which by the way had the same absurd filesystem as Linux still has) but cleaned it up. First thing to be cleaned was (drumroll) the filesystem. But that’s another story all together.

When I entered the Puppy Linux forum I naturally mentioned that I was a complete total Linux novice, and that my favorite machine before x86 was an Amiga. And what do you think happened? Let’s just say that more than a few greeted me with open arms. These were the Amiga users that went to Linux when Commodore went under all those years ago. And they had been active in shaping Linux ever since (!)

So yeah, had a great time on their forums – and it was like running into your long-lost cousin or something. Like if you havent seen a family member in 30 years and suddenly you meet them face to face.

Tired of 30 gigabyte operatingsystems?

Puppy Linux is not for everyone. It’s the kind of system you either love or hate. I have yet to find someone on a middle-ground regarding puppy. Either you love it, or you hate it. Or if you prefer: either you use it and are thrilled about it, or you never install it.

It has a lot of good things going for it:

  • It is built to be one of the smallest, working desktop environment you can get
  • It is built according to “the old ways”, where speed, efficiency and size matter
  • It runs fine on older hardware (my test machine is an 8 year old laptop) and makes stuff you would otherwise throw away become valuable again.
  • It is storage abstracted, meaning you can have all your personal stuff inside a single SFS archive (easier to back up), while the operatingsystem remains on a USB stick.
  • You don’t have to permanently install it (again, boot from a USB stick).
  • It is single user by default, which is perfect for IOT projects and devices!
  • It supports ARM, so you can now enjoy this awesome thing on Raspberry PI 3 !
  • Its Linux so it has all the benefits of a rich driver database
  • Latest Puppy is binary compatible with Ubuntu (whatever that means)
  • There are 3 different desktops for it (to my knowledge), so if you don’t like the default client just install something else
  • It is the perfect rescue USB stick. At less than 300 Mb you can fit it on any old USB stick you have around the house. I think the smallest you can buy now is 4 GB
  • It has a warm, helpful, friendly and international group of users

Oh and it’s free!

As a final note: I installed Wine, the system that makes it possible to run Windows software on Linux (not an emulator, more of a api-call middle-ware /slash/ dispatcher). I was quite surprised to see it run Smart Mobile Studio on the first try!

So fancy a bit of hacking this weekend? Why not give puppy a go?

Check it out here: http://puppylinux.org/main/Download%20Latest%20Release.htm

Smart Pascal + WebOS = true

April 15, 2017 Leave a comment

LG-WebOSIf you own a television (and who doesn’t) chances are you own an LG model. The LG brand has been on the market since before recorded history (or so it feels) and have been a major player for decades.

What is little known though, is that LG also owns and finance a unique operating system. This is not uncommon these days, most NAS and cloud vendors have their own system (there are roughly 20 cloud based systems on the market that I know of). The only way to make a Smart TV (sigh) is to add a computer to it. And if you buy a television today chances are it contains a small embedded board running some custom-made operating system designed to do just that.

Every vendor has their own system, and those that don’t usually end up forking Android and adapt that to their needs.

Luna WebOS

LG’s system is called WebOS. This may sound like yet another html eye candy front end, but hear me out because this OS is not what it seems.

VirtualBox_Developers LuneOS emulator appliance 20151006131924-stable-038-253_15_04_2017_16_01_43

Except for some font issues (see disk label) Smart loaded fine under WebOS

Remember Palm OS? If you are between 35 and 45 you should remember that before Apple utterly demolished the mobile-phone market with their iPhone, one of the most popular brands was Palm. Their core product being “Palm Pilot”, a kind of digital filo-fax (personal organizer) and mobile phone rolled into one.

WebOS is based on what used to be called Palm-OS, but it has been completely revamped, given a sexy new user interface (looks a lot like android to be honest!) and brought into the present age. And best of all: its 100% free! It runs on a plethora of systems, from x86 to Raspberry PI to Mips. It is used in televisions, terminals and set-top-boxes around the world – and is quite popular amongst engineers.

Check it out their new portal here; there is also plenty of links to pre-built images if you look around there: https://pivotce.com/

JavaScript application stack

One of the coolest features in my view, is that their applications are primarily made by JavaScript. The OS itself is native of course, but they have discovered that JavaScript and HTML5 is an excellent way to build applications. Applications that is easy to control, sandboxed and safe yet incredibly powerful and capable.

VirtualBox_Developers LuneOS emulator appliance 20151006131924-stable-038-253_15_04_2017_16_00_46

Smart Desktop booting Quake 3 in Luna OS

Well, today I sent them an Email requesting their SDK. I know they use Enyo.js as their primary and suggested framework – but that is no problem for Smart Mobile Studio. A better question is: can Luna handle our codebase?

When I loaded up Quake 3 in pure Asm.JS the TV crashed with a spectacular access violation. So they are probably not used to the level of hardcore coding Smart developers represent. But yeah, Quake III is about as advanced as it gets – and it pushes any browser to the outer limits of what is possible.

Once I get the SDK, docs and a few examples – I will begin adding support for it as quickly as possible. Which means you get a new project type especially for the platform + API units (typically stored under $RTL\Apis folder).

Why is this cool?

Because with support for Luna / WebOS, you as a Delphi or Smart developer can offer your services to companies that use WebOS or Luna (the open source version) in their devices. The list of companies is substantial – and they are well established companies. And as you have no doubt noticed, hardware engineers doesn’t always make the best software engineers. So this opens up plenty of opportunities for a good object pascal / Smart developer.

17948597_10154372538120906_1912877833_o

Luna has an Android like quality over it – except its more smooth to use

Remember – other developers will use vanilla JavaScript. You have the onslaught of the VJL and the might of our libraries at your disposal. You can produce in days what others use weeks on achieving.

These are exciting days! I’ll keep you posted on our progress!

Smart Pascal, the next generation

April 15, 2017 Leave a comment

I want to take the time to talk a bit about the future, because like all production companies we are all working towards lesser and greater goals. If you don’t have a goal then you are in trouble; Thankfully our goals have been very clear from the beginning. Although I must admit that our way there has been.. “colorful” at times.

When we started back in 2010 we didn’t really know what would become of our plans. We only knew that this was important; there was a sense of urgency and “we have to build this” in the air; I think everyone involved felt that this was the case, without any rational explanation as to why. Like all products of passion it can consume you in a way – and you work day and night on turning an idea into something real. From the intangible to the tangible.

transitions_callback

It seems like yesterday, but it was 5 years ago!

By the end of 2011 / early 2012, Eric and myself had pretty much proven that this could be done. At the time there were more than enough nay-sayers and I think both of us got flamed quite often for daring to think different. People would scoff at me and say I was insane to even contemplate that object pascal could ever be implemented for something as insignificant and mediocre as JavaScript. This was my first meeting with a sub-culture of the Delphi and C++ community, a constellation I have gone head-to-head with on many occasions. But they have never managed to shake my resolve as much as inch.

 

 

When we released version 1.0 in 2012 some ideas about what could be possible started to form. Jørn defined plans for a system we later dubbed “Smart net”. In essence it would be something you logged onto from the IDE – allowing you to store your projects in the cloud, compile in the cloud (connected with Adobe build services) and essentially move parts of your eco-system to the cloud. Keep in mind this was when people still associated cloud with “storage”; they had not yet seen things like Uber or Netflix or played Quake 3 at 160 frames per second, courtesy of asm.js in their browser.

The second part would be a website where you could do the same, including a live editor, access to the compiler and also the ability to buy and sell components, solutions and products. But for that we needed a desktop environment (which is where the Quartex Media Desktop came in later).

cool

The first version of the Media Desktop, small but powerful. Here running in touch-screen mode with classical mobile device layout (full screen forms).

Well, we have hit many bumps along the road since then. But I must be honest and say, some of our detours have also been the most valuable. Had it not been for the often absurd (to the person looking in) research and demo escapades I did, the RTL wouldn’t be half as powerful as it is today. It would deliver the basics, perhaps piggyback on Ext.js or some lame, run of the mill framework – and that would be that. Boring, flat and limited.

What we really wanted to deliver was a platform. Not just a website, but a rich environment for creating, delivering and enjoying web and cloud based applications. And without much fanfare – that is ultimately what the Smart Desktop and it’s sexy node.js back-end is all about is all about.

We have many project types in the pipeline, but the Smart Desktop type is by far the most interesting and powerful. And its 100% under your control. You can create both the desktop itself as a project – and also applications that should run on that desktop as separate projects.

This is perfectly suited for NAS design (network active storage devices can usually be accessed through a web portal on the device), embedded boards, intranets and even intranets for that matter.

You get to enjoy all the perks of a multi-user desktop, one capable of both remote desktop access, telnet access, sharing files and media, playing music and video (we even compiled the mp4 codec from C to JavaScript so you can play mp4 movies without the need for a server backend).

The Smart Desktop

The Smart Desktop project is not just for fun and games. We have big plans for it. And once its solid and complete (we are closing in on 46% done), my next side project will not be more emulators or demos – it will be to move our compiler(s) to Amazon, and write the IDE itself from scratch in Smart Pascal.

smart desktop

The Smart Desktop – A full desktop in the true sense of the word

And yeah, we have plans for EmScripten as well – which takes C/C++ and compiles it into asm.js. It will take a herculean effort to merge our RTL with their sandboxed infrastructure – but the benefits are too great to ignore.

As a bonus you get to run native 68k applications (read: Amiga applications) via emulation. While I realize this will be mostly interesting for people that grew up with that machine – it is still a testament to the power of Smart and how much you can do if you really put your mind to it.

Naturally, the native IDE wont vanish. We have a few new directions we are investigating here – but native will absolutely not go anywhere. But cloud, the desktop system we are creating, is starting to become what we set out to make five years ago (has it been half a decade already? Tempus fugit!). As you all know it was initially designed as an example of how you could write full-screen applications for Raspberry PI and similar embedded devices. But now its a full platform in its own right – with a Linux core and node.js heart, there really is very little you cannot do here.

scsc

The Smart Pascal compiler is one of our tools that is ready for cloud-i-fication

Being able to login to the Smart company servers, fire up the IDE and just code – no matter if you are: be it Spain, Italy, Egypt, China or good old USA — is a pretty awesome thing!

Clicking compile and the server does the grunt work and you can test your apps live in a virtual window; switch between device layouts and targets — then hit “publish” and it goes to Cordova (or Delphi) and voila – you get a message back when binaries for 9 mobile devices is ready for deployment. One click to publish your applications on Appstore, Google play and Microsoft marketplace.

Object pascal works

People may have brushed off object pascal (and from experience those people have a very narrow view of what object pascal is all about), but when they see what Smart delivers, which in itself is written in Delphi, powered by Delphi and should be in every Delphi developer’s toolbox — i think it should draw attention to both Delphi as a product, object pascal as a language – and smart as a solution.

With Smart it doesn’t matter what computer you use. You can sit at home with the new A1222 PPC Amiga, or a kick-ass Intel i7 beast that chew virtual machines for breakfast. If your computer can handle a modern website, then you can learn object pascal and work directly in the cloud.

desktop_embedded

The Smart Desktop running on cheap embedded hardware. The results are fantastic and the financial savings of using Smart Pascal on the kiosk client is $400 per unit in this case

Heck you can work off a $60 ODroid XU4, it has more than enough horsepower to drive the latest chrome or Firefox engines. All the compilation takes place on the server anyways. And if you want a Delphi vessel rather than phonegap (so that it’s a Delphi application that opens up a web-view in full-screen and expose features to your smart code) then you will be happy to know that this is being investigated.

More targets

There are a lot of systems out there in the world, some of which did not exist just a couple of years ago. FriendOS is a cloud based operating system we really want to support, so we are eager to get cracking on their SDK when that comes out. Being able to target FriendOS from Smart is valuable, because some of the stuff you can do in SMS with just a bit of code – would take weeks to hand write in JavaScript. So you get a productive edge unlike anything else – which is good to have when a new market opens.

As far as Delphi is concerned there are smaller systems that Embarcadero may not be interested in, for example the many embedded systems that have come out lately. If Embarcadero tried to target them all – it would be a never-ending cat and mouse game. It seems like almost every month there is a new board on the market. So I fully understand why Embarcadero sticks to the most established vendors.

ov-4f-img

Smart technology allows you to cover all your bases regardless of device

But for you, the programmer, these smaller boards can repsent thousands of dollars worth of saving. Perhaps you are building a kiosk system and need to have a good-looking user interface that is not carved in stone, touch capabilities, low-latency full-duplex communication with a server; not much you can do about that if Delphi doesnt target it. And Delphi is a work horse so it demands a lot more cpu than a low-budget ARM SoC can deliver. But web-tech can thrive in these low-end environments. So again we see that Smart can compliment and be a valuable addition to Delphi. It helps you as a Delphi developer to act on opportunities that would otherwise pass you by.

So in the above scenario you can double down. You can use Smart for the user-interface on a low power, low-cost SoC (system on a chip) kiosk — and Delphi on the server.

It all depends on what you are interfacing with. If you have a full Delphi backend (which I presume you have) then writing the interface server in Delphi obviously makes more sense.

If you don’t have any back-end then, depending on your needs or future plans, it could be wise to investigate if node.js is right for you. If it’s not – go with what you know. You can make use of Smart’s capabilities either way to deliver cost-effective, good-looking device front-ends of mobile apps. Its valuable tool in your Delphi toolbox.

Better infrastructure and rooting

So far our support for various systems has been in the form of APIs or “wrapper units”. This is good if you are a low-level coder like myself, but if you are coming directly from Delphi without any background in web technology – you wouldn’t even know where to start.

So starting with the next IDE update each platform we support will not just have low-level wrapper units, but project types and units written and adapted by human beings. This means extra work for us – but that is the way it has to be.

As of writing the following projects can be created:

  • HTML5 mobile applications
  • HTML5 mobile console applications
  • Node.js console applications
  • node.js server applications
  • node.js service applications (requires PM2)
  • Web worker project (deprecated, web-workers can now be created anywhere)

We also have support for the following operating systems:

  • Chrome OS
  • Mozilla OS
  • Samsung Tizen OS

The following API’s have shipped with Smart since version 1.2:

  • Khronos browser extensions
  • Firefox spesific API
  • NodeWebkit
  • Phonegap
    • Phonegap provides native access to roughly 9 operating systems. It is however cumbersome to work with and beta-test if you are unfamiliar with the “tools of the trade” in the JavaScript world.
  • Whatwg
  • WAC Apis

Future goals

The first thing we need to do is to update and re-generate ALL header files (or pascal units that interface with the JavaScript libraries) and make what we already have polished, available, documented and ready for enterprise level use.

kiosk-systems

Why pay $400 to power your kiosk when $99 and Smart can do a better job?

Secondly, project types must be established where they make sense. Not all frameworks are suitable for full project isolation, but act more like utility libraries (like jQuery or similar training-wheels). And as much as possible of the RTL made platform independent and organized following our namespace scheme.

But there are also other operating systems we want to support:

  • Norwegian made Friend OS, which is a business oriented cloud desktop
  • Node.js OS is very exciting
  • LG WebOS, and their Enyo application framework
  • Asustor DLM web operating system is also a highly attractive system to support
  • OpenNAS has a very powerful JavaScript application framework
  • Segate Nas OS 4 likewise use JavaScript for visual, universal applications
  • Microsoft Universal Platform allows you to create truly portable, native speed JavaScript applications
  • QNap QTS web operating system [now at version 4.2]

All of these are separate from our own NAS and embedded device system: Smart Desktop, which uses node.js as a backend and will run on anything as long as node and a modern browser is present.

Final words

I hope you guys have enjoyed my little trip down memory lane, and also the plans we have for the future. Personally I am super excited about moving the IDE to the cloud and making Smart available 24/7 globally – so that everyone can use it to design, implement and build software for the future right now.

Smart Pascal Builder (or whatever nickname we give it) is probably the first of its kind in the world. There are a ton of “write code on the web” pages out there, but so far there is not a single hard-core development studio like I have in mind here.

So hold on, because the future is just around the corner 😉

Delphi developer on its own server

April 4, 2017 Leave a comment

While the Facebook group will naturally continue exactly like it has these past years, we have set up a server active Delphi developers on my Quartex server.

This has a huge benefit: first of all those that want to test the Smart Desktop can do so from the same domain – and people who want to test Smart Mobile Studio can do so with myself just a PM away. Error reports etc. will still need to be sent to the standard e-mail, but now I can take a more active role in supervising the process and help clear up whatever missunderstanding could occur.

casebook

Always good with a hardcore Smart, Laz, amibian.js forum!

Besides that we are building a lively community of Delphi, Lazarus, Smart and Oxygene/Remobjects developers! Need a job? Have work you need done? Post an add — wont cost you a penny.

So why not sign up? Its free, it wont bite you and we can continue regardless of Facebook’s up-time this year..

You enter here and just fill out user/pass and that’s it: http://quartexhq.myasustor.com/sharetronix/

Smart Pascal: Information to alpha testers

April 3, 2017 Leave a comment

Note: This is re-posted here since we are experiencing networking problems at http://www.smartmobilestudio.com. The information should show up there shortly.

Our next-gen RTL will shortly be sent to people who have shown interest in testing our new RTL. We will finish the RTL in 3 stages of alpha before we hit beta (including code freeze, just fixes to existing code) and then release. After that we move on to the IDE to bring that up to date as well.

Important changes

You will notice that visual controls now have a ton of new methods, but one very interesting in particular called: ObjectReady. This method holds an important and central role in the new architecture.

You may remember that sometimes you had to use Handle.ReadyExecute() in the previous RTL? Often to synchronize an activity, setting properties or just calling ReSize() when the control was ready and available in the DOM.

To make a long story short, ObjectReady() is called when a control has been constructed in full and the element is ready to be used. This also includes the ready-state of child elements. So if you create 10 child controls, ObjectReady will only be called once those 10 children have finished constructing – and the handle(s) have safely been injected into the DOM.

To better understand why this is required, remember that JavaScript is asynchronous. So even though the constructor finish – child objects can still be busy “building” in the background.

If you look in SmartCL.Components.pas, notice that when the constructor finishes –it does a asynchronous call to a procedure named ReadySync(). This is a very important change from the previous version – because now synchronization can be trusted. I have also added a limit to how long ReadySync() can wait. So if it takes to long the ReadySync() method will simply exit.

ObjectReady

As you probably have guessed, when ReadySync() is done and all child elements have been successfully created – it called the ObjectReady() method.

This makes things so much easier to work with. In many ways it resembles Delphi and Freepascal’s AfterConstruction() method.

To summarize the call-chain (or timeline) all visual controls follow as they are constructed:

  • Constructor Create()
  • InitializeObject()
  • ReadySync
  • ObjectReady()
  • Invalidate
  • Resize

If you are pondering what on earth Invalidate() is doing in a framework based on HTML elements: it calls Resize() via the RequestAnimationFrame API. TW3GraphicControl visual controls that are actually drawn, much like native VCL or LCL components are – naturally invalidate the graphics in this method. But ordinary controls just does a synchronized resize (and re-layout) of the content.

When implementing your own visual controls (inheriting from TW3CustomControl), the basic procedures you would have to override and implement are:

  • InitializeObject;
  • FinalizeObject;
  • ObjectReady;
  • Resize;

Naturally, if you dont create any child controls or data of any type – then you can omit InitializeObject and FinalizeObject; these act as constructor and destructor in our framework. So in the JVL (Javascript Visual component Library) you dont override the constructor and destructor directly unless it is extremely important.

Where to do what

What the JVL does is to ensure a fixed set of behavioral traits in a linear fashion- inside an environment where anything goes. In order to achieve that the call chain (as explained above) must be predictable and rock solid.

Here is a quick cheat sheet over what to do and where:

  • You create child instances and set variables in InitializeObject()
  • You set values and access the child instances for the first time in ObjectReady()
  • You release any child instances and data in FinalizeObject()
  • You enable/disable behavior in CreationFlags()
  • You position and place child controls in Resize()
  • Calling Invalidate() refreshes the layout or graphics, depending on what type of control you are working with.

What does a custom control look like ?

Its actually quite simple. Now I have included a ton of units here in order to describe what they contain, then you can remove those you wont need. The reason we have fragmented the code like this (for example System.Reader, System.Stream.Reader and so on) is because node.js, Arduino, Raspberry PI, Phonegap, NodeWebKit are all platforms that run JavaScript in one form or another – and each have code that is not 1:1 compatible with the next.

Universal code, or code that executes the same on all platforms is isolated in the System namespace. All files prefixed with “System.” are universal and can be used everywhere, regardless of project type, target or platform.

When it comes to the reader / writer classes, it’s not just streams. We also have binary buffers (yes, you actually have allocmem() etc. in our RTL) – but you also have special readers that work with database blobs, Bson attachments .. hence we had no option but to fragment the units. At least it makes for smaller code 🙂

unit MyOwnControlExample;

interface

uses
  // ## The System namespace is platform independent
  System.Widget,           // TW3Component
  System.Types,            // General types
  System.Types.Convert,    // Binary access to types
  System.Types.Graphics,   // Graphic types (TRect, TPoint etc)
  System.Colors,           // TColor constants + tools
  System.Time,             // TW3Dispatch + time methods

  // Binary data and streams
  System.Streams,
  System.Reader, System.Stream.Reader,
  System.Writer, System.Stream.Writer,

  // Binary data and allocmem, freemem, move, copy etc
  System.Memory,
  System.Memory.Allocation,
  System.Memory.Buffer,

  // ## The SmartCL namespace works only with the DOM
  SmartCL.System,         // Fundamental methods and classes
  SmartCL.Time,           // Adds requestAnimationFrame API to TW3Dispatch
  SmartCL.Graphics,       // Offscreen pixmap, canvas etc.
  SmartCL.Components,     // Classes for visual controls
  SmartCL.Effects,        // Adds about 50 fx prefixed CSS3 GPU effect methods
  SmartCL.Fonts,          // Font and typeface control
  SmartCL.Borders,        // Classes that control the border of a control
  SmartCL.CSS.Classes,    // Classes for self.css management
  SmartCL.CSS.StyleSheet, // Create stylesheets or add styles by code

  { Typical child controls
  SmartCL.Controls.Image,
  SmartCL.Controls.Label,
  SmartCL.Controls.Panel,
  SmartCL.Controls.Button,
  SMartCL.Controls.Scrollbar,
  SMartCL.Controls.ToggleSwitch,
  SmartCL.Controls.Toolbar }
  ;

type

  TMyVisualControl = class(TW3CustomControl)
  protected
    procedure InitializeObject; override;
    procedure FinalizeObject; override;
    procedure ObjectReady; override;
    procedure Resize; override;
  end;

implementation

procedure TMyVisualControl.InitializeObject;
begin
  inherited;
  // create child instances here
end;

procedure TMyVisualControl.FinalizeObject;
begin
  // Release child instances here
  inherited;
end;

procedure TMyVisualControl.ObjectReady;
begin
  inherited;
  // interact with controls first time here
end;

procedure TMyVisualControl.Resize;
begin
  inherited;
  if not (csDestroying in ComponentState) then
  begin
    // Position child elements here
  end;
end;

CreateFlags? What is that?

Delphi’s VCL and Lazarus’s LCL have had support for CreateFlags for ages. It essentially allows you to set some very important properties when a control is created; properties that enable or disable how the control behaves.

  TW3CreationFlags = set of
    (
    cfIgnoreReadyState,     // Ignore waiting for readystate
    cfSupportAdjustment,    // Controls requires boxing adjustment (default!)
    cfReportChildAddition,  // Dont call ChildAdded() on element insertion
    cfReportChildRemoval,   // Dont call ChildRemoved() on element removal
    cfReportMovement,       // Report movements? Managed call to Moved()
    cfReportResize,         // Report resize? Manages call to Resize()
    cfAllowSelection,       // Allow for text selection
    cfKeyCapture            // flag to issue key and focus events
    );

As you can see these properties are quite fundamental, but there are times when you want to alter the default behavior radically to make something work. Child elements that you position and size directly doesn’t need cfReportReSize for instance. This might not mean much if its 100 or 200 child elements. But if its 4000 rows in a DB grid, then dropping that event check has a huge impact.

ComponentState

Yes, finally all visual (and non visual) have componentstate support. This makes your code much more elegant, and also way more compatible with Delphi and Lazarus. Here are the component-states we support right now:

  TComponentState = set of (
    csCreating,   // Set by the RTL when a control is created
    csLoading,    // Set by the RTL when a control is loading resources
    csReady,      // Set by the RTL when the control is ready for use
    csSized,      // Set this when a resize call is required
    csMoved,      // Set this when a control has moved
    csDestroying  // Set by the RTL when a control is being destroyed
    );

So now you can do things like:

procedure TMyControl.StartAnimation;
begin
  // Can we do this yet?
  if (csReady in ComponentState) then
  begin
    // Ok do magical code here
  end else
  //If not, call back in 100ms - unless the control is being destroyed
  if not (csDestroying in ComponentState) then
    TW3Dispatch.Execute(StartAnimation, 100);
end;

Also notice TW3Dispatch. You will find this in System.Time and SmartCL.Time (it is a partial class and can be expanded in different units); we have isolated timers, repeats and delayed dispatching in one place.

SmartCL.Time adds RequestAnimationFrame() and CancelAnimationFrame() access, which is an absolute must for synchronized graphics and resize.

Other changes

In this post I have tried to give you an overview of immediate changes. Changes that will hit you the moment you fire up Smart with the new RTL. It is very important that you change your existing code to make use of the ObjectReady() method in particular. Try to give the child elements some air between creation and first use – you get a much more consistent result on all browsers.

The total list of changes is almost to big to post on a blog but I did publish a part of it earlier. But not even that list contains the full extent. I have tried to give you an understanding

Click here to look at the full change log

Smart Pascal: Busting browser storage limits

April 2, 2017 Leave a comment

Sessionstorage is the name for a browser’s in-memory only storage. Meaning that it’s essentially a ram-disk that is just deleted when you navigate away from the website or close the browser.

Sessionstorage has also been deprecated, so you should avoid using it and go for Localstorage, or just use a raw, untyped uint8 array instead.

Or should you?

Ensuring 64 megabytes minimum

Browsers do not behave identically across devices. Try to get a concurrent reading of something as simple as drawing sprites, and you will quickly notice that even the same device families (Android, iOS and Microsoft) can behave differently between versions – and even builds (revisions).

On embedded systems or thin clients with very little memory, allocating large chunks ot uint8 arrays is not going to work. One of my test thin-client machines has only 512 megabyte ram – and it would throw an exception if I tried to allocate more than 20 megabyte of continuous memory (again, as an array of uint8 bytes).

Using the dark side of the force

Screenshot

Offline means the system boots from a local cache disk

While testing Smart code on this little device, I noticed that quite large images loaded just fine. So where I was not allowed to allocate more than 20 megabytes, the browser would happily load in pictures taking up over 50 megabyte of pixel data?

It then struck me that the maximum limit of a picture, which is enforced by the DIB Api (at least on Windows desktop and embedded), is 4000 x 4000 pixels. Since each pixel is 32 bits (4 bytes, RGBA) that my friend is 64 megabytes right there!

I created a new class that inherits from the virtual-filesystem that Smart Pascal uses, created an off-screen image object in the constructor – and then made a simple but effective “bytes to scan line” calculation routine. So whenever the need for more data grew, it would first grow the picture so it could hold the data (and shrink it again) on demand.

Humble but meaningful

Now 64 megabytes might not seem like much in our day and age, but if you are on holiday and want to connect to your home NAS – 64 megabytes of available ram makes a huge difference. Remember that localstorage only allows between 5 and 10 megabytes.

I should mention that using an image as a buffer makes little sense on a full Windows PC, a Mac or a Linux box. These system will page memory to disk and you will most likely never encounter the 20 megabyte barrier I experienced on this low-end Dell thin client device. But considering that hotels, motels and b&b often have thin clients setup for their customers (read: you) – The Smart desktop has to take height for it.

 

 

 

 

 

Smart desktop: Amibian.js past, future and present

April 1, 2017 2 comments

Had someone told me 20 years ago that I would enter my 40’s enjoying JavaScript, my younger self would probably have beaten that someone over the head with a book on hardcore demo coding or something. So yeah, things have changed so much and we are right now in the middle of a paradigm shift that is taking us all to the next level – regardless if we agree or not.

Ask a person what he thinks about “cloud” and you rarely get an answer that resembles what cloud really is. Most will say “its a fancy way of dealing with storage”. Others will say its all about web-services – and others might say it’s about JavaScript.

memyselfandi

Old coders never die, we just get better

They are all right and wrong at the same time. Cloud is first of all the total abstraction of all parts that makes up a networked computer system. Storage, processing capacity, memory, operating system, services, programming language and even instruction set.

Where a programmer today works with libraries and classes to create programs that run on a desktop — dragging visual controls like edit-boxes and buttons into place using a form or window designer; a cloud developer builds whole infrastructures. He drags and drops whole servers into place, connects external and internal services.

Storage? Ok I need Dropbox, amazon, Google drive, Microsoft one disk, local disk – and he just drags that onto a module. Done. All of these services now have a common API that allows them to talk with each other. They become like dll files or classes, all built using the same mold – but with wildly different internals. It doesn’t matter as long as they deliver the functionality according to standard.

Processing power? Setup an Azure or Amazon account and if you got the cash, you can compute enough to pre-cacalculate the human brain if you like. It has all been turned into services — that you can link together like lego pieces.

Forget everything you think you know about web; that is but the visual rendering engine over the proverbial death-star of technology hidden beneath. People have only seen the tip of the ice berg.

Operating systems have been reduced to a preference. There is no longer any reason to pick Windows over Linux on the server. Microsoft knew years ago that this day would come. Back in the late 90s I remember reading an interview with Steve Balmer doing one of his black-ops meetings with Norwegian tech holders in Oslo; and he outlined software as a service when people were on 14.4 modems. He also mentioned that “we need a language that is universal” to make this a reality. You can guess why .net was stolen from Borland, not to mention their failed hostile takover of Java (or J#) which Anders Hejlsberg was hired to spear-head.

Amibian.js

Amibian.js is my, Gunnar and Thomas‘s effort to ensure that the Amiga is made portable and can be enjoyed in this new paradigm. It is not made to compete with anyone (as was suggested earlier), but rather to make sure Amiga gets some life into her again – and that people of this generation and the kids growing up now can get to enjoy the same exciting environment that we had.

Amibian666

From Scandinavia with love

The world is going JavaScript. Hardware now speaks JavaScript (!), your TV now speaks JavaScript – heck your digital watch probably runs JavaScript. And just to add insult to injury – asm.js now compiles JS code side-by-side with C/C++ in terms of speed. I think the browser has seen more man years of development time than any other piece of software out there – with the exception of GCC / Gnu Linux perhaps.

Amibian is also an example of a what you can do with Smart Pascal, which is a programming environment that compiles object pascal to JavaScript. One we (The Smart Company AS) made especially for this new paradigm. I knew this was coming years ago – and have been waiting for it. But that’s another story all together.

Future

Well, naturally the desktop system is written from scratch so it needs to be completed. We are at roughly 40% right now. But there is also work to be done on UAE.js (a mild fork of sae, scriptable Amiga emulator) in terms of speed and IO. We want the emulated Amiga side to interact with the modern web desktop – to be able to load and save files to whatever backend you are using.

 

Amibianstuff

For those about to rock; We salute you!

Well, it’s not that easy. UAE is like an organism, and introducing new organs into an existing creature is not easily done. UAE.js (or SAE) has omitted a huge chunk of the original code – which also includes the modified boot-code that adds support for external or “virtual” UAE drives (thanks to Frode Solheim of Fs-UAE for explaining the details of the parts here).

But, there are hacker ways. The dark side is a pathway to many abilities, some deemed unnatural. So if all else fails, i will kidnap the hardfile API and delegate the IO signals to the virtual filesystem on the server — in short, UAE.JS will think it’s booting from a hardfile in memory – when in reality it will get its data from node.js.

There are some challenges here. UAE (the original) is not async but ordinary, linear C code. JavaScript is async and may not return the data on exit of the method. So i suspect I will have to cache quite a lot. Perhaps as much as 1 megabyte backwards and forwards from the file-position. But getting data in there we will (and out), come hell or high water.

We can also drop a lot of sub code, like parts of the gayle interface. I found out this is the chip that deals with the IDE interface — so it has to be there, but it can also host memory expansions – but who the hell cares in 2017 in JavaScript about that. More than enough fun via standard chip/fast/rtg memory – so the odd bits can be removed.

So we got our work cut out for us. But hey.. there can only be one .. QUARTEX! And we bring you fire.

Ok. Lets do this!

Node and delphi in sweet harmony

March 31, 2017 Leave a comment

There are cases when you want to dip your toe into Delphi land. While I can’t think of anything in particular at the moment – it would be handy if you say, have some Delphi dll’s that you would like to re-use, so you don’t have to re-write the whole thing in Smart Pascal.

Enter FFI

There is this mind-blowing package over at NPM and if you don’t know it yet, you are going to love it. Its called FFI (foreign function interface) and essentially allows you to call functions from ordinary libraries (DLL, .SO and .DynLib’s). It is supported on both Windows, OS X and Linux. So yeah, now you can call Delphi from your Smart code!

So let’s say you have this awesome data backend written in Delphi and you don’t want to re-write 10 years of code. What you can do is simply create a DLL and expose a simplified API for the core functionality — and then call those from your Smart Pascal node.js server — which is just mind-bending awesome (excuse my language)!

You can also call functions exposed in the same process. So lets say you are calling node.js from your Delphi applications (or C++, or Freepascal or C# – whatever floats your boat) then you guessed it – you can now invoke methods from the “runner”.

There goes another weekend

I was going to relax this weekend but im busy compiling node.js from scratch (and you know how I feel about C++ from Linux, its like the worst date ever!) to something visual studio can eat – and then export it from that into Delphi. For some reason C++ builder goes off bonkers when I try to build it there. Which is so odd because it should be vanilla C/C++ (probably some gnu curiosity messing it up)

 

kick_ass

Create awesome full browser-desktop apps in Smart!

 

But once .DCU i-fied, how does a TNodeJS component sound? Already have TV8Engine – so yeah, interfacing with the mothership (read: Delphi) is important to us.

Anyways – if your brain didn’t just blow with the FFI info, then go and get all girly inside right now – because this bridges the gap so utterly between native and scalable, kick ass clustered JavaScript server-side!

https://www.npmjs.com/package/ffi

If you still wonder if this is powerful enough, check out the Smart based operating system that is currently kicking serious butt! It even runs 68k assembly programs (real Amiga applications) in a window! This is the core demo for the SMS update (it should have been out 2 weeks ago, I know – but its not me in charge of that bit!).

amibianrocks

An OS in smart? Sure, with a linux bootstrap ofcourse – but man does it run!

Oh and by the way, Smart Pascal now gives you access to more than 350.000 node.js packages. That is the biggest, bad-ass code-repo in existence. And you can then add typescript as well.

Ok let’s do this!

HTMLComponents, the native Delphi html rendering engine

March 26, 2017 1 comment

This review has been postponed two times, so my apologies to Alexander Sviridenkov, the author of this magnificent Delphi component package.

HTML rendering engine in Delphi

Delphi have always had its fair share of brilliant programmers. And as a result Delphi has enjoyed a vibrant component market, one that simplify the life of both novice and expert.

Sadly, html has never really been Delphi’s strong suit. Sure we have various server types and packages. From Intraweb to UniGui – each of them with positive and negative traits. We have IIS plugin packages and apache modules – and right now Embarcadero is branching into Linux as well. Sadly all of these solutions suffer the same problem: they are purely native. They lack the automatic scaling mechanisms of node.js and C# – and don’t even get me started on clustering (Netflix is written 100% in node.js, why did they pick JavaScript instead of Delphi, C# or C++? Food for thought!).

But, serving html is all well and good, and while I could write a doctorate on why node.js and Smart Pascal is a much better technology for Delphi developers when it comes to web development – it would come across as disingenuous since Smart originated with me. So people will simply have to find out for themselves.

Rendering with Delphi

Server and client aside: if there is one thing Delphi is the undisputed king of, it is desktop applications and productivity! Fueled by object pascal’s modular, component based frameworks; like the VCL and Firemonkey – no other language even comes close to what Delphi has to offer. Except for html that is ..

Traditionally there have been only two paths you could go if you wanted to display or edit HTML: you could stick with good old TWebBrowser, a thin wrapper over Microsoft’s Internet Explorer API’s. If all you want to do is to show some basic html and lack even a single fiber of taste in your body then TWebBrowser could be the ticket.

But anyone who has ever tried to work with that component, perhaps dived into the hundreds of interfaces you need to deal with in order to do anything remotely fun – knows that it leaves you frustrated and wanting. It’s also not portable and as pure a Windows component as can be. It makes absolutely no sense to use this component in 2017, especially when Firemonkey is on the menu.

The other option was TFrameBrowser or THtmlViewer, a fast, responsive browser control written purely in Delphi (by Dave Baldwin I seem to remember?). This was a commercial component back in the day, but he open sourced it as he abandoned Delphi for C# or C++ or something along those lines. You can pick this up at Github for free and there is also a Lazarus version of this.

The problem with both of these solution is the same haunting us all, namely that they are getting old. They havent seen much development for 10-15 years (give or take the port to Lazarus), and while they may work – they lack support for all the interesting tags, css styles and element types that we now take for granted. THtmlView is stuck somewhere between HTML 1 or 2, and TWebBrowser is .. well, that has a life of its own so I honestly cant tell you what it represents these days. But a little bird told me Embarcadero havent updated the ActiveX interfaces at all – so it’s not even using the new Microsoft Edge engine. Personally I suspect Microsoft is the culprit, exposing Edge as a .net assembly exclusively.

The Chromium wrappers

A third alternative appeared around 2011 / 2012 in the form of chromium embedded. This is the browser we use in Smart Mobile Studio to do background rendering and run the compiled Smart Mobile Studio applications when you hit F9.

Webkit is by far the fastest and most evolved engine out there, but it also add around 90 – 120 megabyte of dependencies and data files to your distribution. It also has a lot of quirks, and if you do hardcore coding like I do – pushing the GPU to the bleeding edge of what webkit can deliver – then prepare for crashes, unexplainable access violations and having to restart the whole application to get rid of some background process chromium has spawned outside your reach. It’s a great solution, but it comes with its own baggage and history.

HTMLComponents

Suddenly out of the blue a fourth option has emerged, this time it’s a full re-implementation of HTML 4 (with elements from HTML 5) written in pure, platform independent Delphi. This means it has no dependencies, it will render as much as 90% of modern html, it supports all the fancy tags and css style (including transitions, which I must admit I did not expect).

Not only that, it works under the traditional VCL framework and Firemonkey, so you can use the components on both desktop and mobile applications. If that doesn’t get your creative juices flowing I don’t know what will; this is frikkin awesome!

Alexander Sviridenkov has really gone to town with this package. And I must extend my gratitude for allowing me access to his codebase. We are considering using this as the primary rendering engine for the new Smart Mobile Studio IDE’s live editor. So I want to personally thank Alexander for the opportunity to investigate the components at source level.

Writing a review of this great package seems only natural. It has been years since I’ve been this stoked about a Delphi component package. If you like the idea of using HTML as a part of your Delphi application’s presentation layer – then consider this your birthday, Xmas and st. patrick day all rolled into one. HtmlComponents got you covered!

So what is it?

component_list

A rich and powerful component suite

At the heart of the package is a modern, fast, portable and 100% written in Delphi HTML rendering engine. Around this engine Alexander has made a large collection of visual controls that you use to enrich your applications. Most notably, a WYSIWYG editor akin to what you find in Adobe Dreamweaver. This is not a text editor where you write tags (well you can), but rather a full, rich word processor control where you can design your web pages.

The components most interesting to me were obviously: THtmlEditor, TDBHtmlEditor, THtTemplatePanel and THtMetroPanel, THtCategoryButtons.

These are just  5 out of 23 visual controls that ship with this package, each of them using the rendering engine to present better looking information. I mean, when you can use html to describe how each row in a listbox should appear – it goes without saying that this is a lot easier (and prettier) than spending hours with TCanvas. The now ancient TCanvas class is not exactly renowned for its blistering speed or fantastic visuals). So HtmlComponents gives you an impressive list of visual extravaganza to play with, all of whom makes it a snap to create modern, good-looking user interfaces.

And you don’t have to use the pre-defined components. HtmlComponents will happily take your html and css and draw it straight to a TCanvas (or GDI+) without complaints. So you are not stuck having to use this or that baseclass (which often is the case).

What really impressed me is the speed. Writing an html file parser that is fast is one thing, but rendering complex and composite web content? That’s not for the faint of heart, it requires some serious skill.

The first and easiest test you can do to see how quickly a rendering engine adapts and re calculate its node tree (internal stuff) – is to resize the window fast back and forth. Most homebrew browsers is going to struggle with that. Typically because they just calculate as they draw each element, which is not how to go about this particular task. I’m super impressed by Alexanders work here, because his controls are just as responsive as commercial browsers.

The editor

Since I work with web technology, the editor is ultimately what would save us time and also help lift the quality of our IDE. Smart Mobile Studio is extremely powerful, but our weak spot is without a doubt that our IDE doesn’t reflect the power of our the RTL properly. There are also things that would be simpler for our customers, editing documents directly is one of them – so the editor was really make or break for my part.

editor

The example editor program is impressive to say the least

The editor is wonderful. There are a few tidbits that I would like to see improved, but those are very specific to my needs. But I have to be honest and say that this is the most impressive WYSIWYG html editor I have seen outside of Dreamweaver. You have full control of everything, from tables to borders, margins to padding – and the active segment you are editing is easy to spot due to dotted highlighting.

Alexander has also made some very interesting dialogs (see color and border dialog in the picture above). While I am missing a more dedicated css stylesheet solution (like gradient editor, image to css conversion and other handy features) – the package in general has more or less everything you need to get going. And then some (it may also be that the stylesheet thing is there. The codebase is so huge it would take s week to study every part).

In short: If you are looking for a drop-in solution for editing web documents that is platform independent, has zero dependencies, lightweight, fast and even work on mobile devices – then this is it! The same goes for rich and gorgeous rendered custom controls.

I do realize that there are other editors out there; packages like Richview is very popular, and while that is both fast and polished, it’s not an html rendering engine. Which is ultimately where HtmlComponents scores all points. The editor is not the core of the product, it’s just a control that exposes the product – which is a modern html rendering system written in pure Delphi.

It even outputs to PDF, again with no dependencies!

What about the web?

Being able to edit html documents is cool and if you are looking for a package to give you that functionality – then this is it. No doubt there. But how compatible is it? I mean, html is evolving at an alarming rate – both in terms of features but also with regards to JavaScript. How does HtmlComponents fare in that department?

I guess the obvious observation is that HtmlComponents is not a browser. It has a very capable rendering engine, editor and supports the most common tags – both old and new, but at the end of the day – it’s not designed to be a browser.

If you drop an editor on a form, set the readonly property to true, and then call LoadFromUrl() on a complex website, then odds are it will come up short. It will happily render static websites, but the moment frames and JavaScript comes into the picture – you are quickly reminded that this is ultimately an editor and rendering engine designed to leverage web technology for Delphi – and it’s not trying to be anything else either. It delivers what it says, and it does it well. What more can you ask.

browser

It has no problems with traditional, non framed or JS powered websites

Lack of scripting

I have no doubt that Alexander could turn this package into a real life browser if he so wanted. But again this package delivers compliant html editing and presentation inside Delphi applications. That is what he set out to create, and that is exactly what I need.

Alexander have also taken height for future development. The package ships with support for pascal scripting (the Jedi scripting engine), and he has thoughtfully isolated scripting in general in separate adapter classes. This means that you are free to add whatever scripting language you want. Dont like pascal script? No problem, just implement an adapter and use whatever you like.

My immediate thought goes to Besen, the open source, complete ECMAScript fifth edition, JIT powered JavaScript engine written 100% in object pascal. Hopefully Alexander will get inspired and add that as an alternative in a future edition. With besen as the primary scripting engine – and taking the time to expose all the traditional DOM objects JavaScript expects to find in a browser environment (like window, document, createElement, getElementById [and so on]) then HtmlComponents would be capable of great things. All of it in portable, platform independent Delphi.

The V8 JavaScript engine used by Chrome, Safari and all webkit browsers is also an alternative. It is a fairly humble dll that is easy to use from Delphi. But that is presently only available on Windows. Unless the Delphi maintainer decides to add Linux and OS X binaries to the repository as well. But indeed, V8 could also be added quite easily.

Final thoughts

This is a wonderful component package for Delphi, one I really hope we get to use in our own product when that time comes. There will always be bits and pieces that could be improved, but it’s quite hard to pinpoint what exactly to criticize in this package.

helpmanual

H&M is pretty solid

I did notice an access violation in the Paint() method on two occasions, which could be easily fixed by checking if csDestroying has been set – and that the device context is not null (it was in the middle of a paint when i killed the program). But that is kebab reporting on my part. All in all I am super impressed and really recommend this product – its worth every penny!

The product is also used by a list of well established companies. Applications like Help & Manual isn’t exactly small potatoes, nor is Coffeecup html editor some unknown application. These are used by thousands of people every day.

Have a look at Alexander’s gallery and I’m sure you agree that this package has merit.

As far as I’m concerned, this will form the basis of our future template and content editor.

Jon-Lennart Aasenden
 The Smart Company AS

Amibian + Smart pascal = A new beginning

March 25, 2017 Leave a comment

In the Amiga community there is a sub-group of people with an agenda. They hoard and collect every piece of hardware they can get their hand on – then sell them at absurd prices on ebay to enrich themselves. This is not only ruining the community at large, ensuring that ordinary users cannot get their hands on a plain, vanilla Amiga without having to fork out enough dough to buy a good car.

“We also have a working Facebook clone – but we’re not
going into competition with Mark Zuckerberg either”

Thankfully not everyone is like that. There are some very respected, very good people who buy old machines, restore them – and sell them at affordable prices. People that do this as a hobby or to make a little on the side. Nothing wrong with that. No, its people that try to sell you an A2000 for $3000, or that pimp out Vampire accelerator cards at 700€ a piece thats the problem.

As for the price sharks, well – this has to stop. And Gunnar’s Amibian distro has already given the Amiga scalpers a serious uppercut. Why buy an old Amiga when you can get a high-end A4000 experience with 4 times as much power for around $35? This is what Gunnar has made a reality – and he deserves a medal for his work!

And myself, Thomas and the others in our little band of brothers will pick up the fight and stand by Gunnar in his battle. A battle to make the Amiga affordable for ordinary human beings that love the platform.

Amiga as a service

Yesterday I did a little experiment. You know how Citrix and VMWare services work? In short, they are virtualization application servers. That means that they can create as many instances of Windows or Linux as they want – run your applications on it – and you can connect to your instance and use it. This is a big part of how cloud computing works.

While my little experiment is very humble, I am now streaming a WinUAE instance display from my basement server pc, just some old bucket of chips I use for debugging, directly into Amibian.js (!). It worked! I just created the world’s first Amiga application server. And it took me less than 30 minutes in Delphi !

Amibian, Amibian.js, appserver, what gives?

Let’s clear this up so you dont mix them:

Amibian. This is the original Linux distro made by Gunnar Kristjansson. It boots straight into UAE in full-screen. All it needs is a Raspberry PI, the Amiga rom files and your workbench or hard disk image. This is a purely native (machine code) solution.

Amibian.js – this is a JavaScript remake of AmigaOS that I’m building, with the look and feel of OS 4. It uses uae.js (also called SAE) to run 68k software directly in the browser. It is not a commercial product, but one of many demos that ship with Smart Mobile Studio. “Smart” is a compiler, editor and run-time library sold by The Smart Company AS. So Amibian.js is just a demo, just like Amos shipped with a ton of demo’s and Blitzbasic came with a whole disk full of examples.

Amiga application server (what I mentioned above) was just a 30 minute experiment I did yesterday after work. Again, just for fun.

I hope that clears up any confusion. Amibian.js is a purely JavaScript based desktop made in the spirit of the Amiga – it must not be confused with Amibian the Linux distro, which boots into UAE on a Raspberry PI. Nor is it an appserver – but rather, it can connect to an appserver if I want to.

genamiga

Generation Amiga post on Amibian.js earlier when I added some look & feel

With a bit of work, and if everything works as expected (I don’t see why not), I will upload both source and binaries to github together with Amibian.js.

There is only one clause: It cannot be used, recreated, included or distributed by Cloanto. Sorry guys, but the ROMS belong to the people, and until you release those into public domain, you wont get access to anything we make. Nothing personal, but pimping out roms and even having audacity to fork UAE and sell it as your own? You should be ashamed of yourself.

Are you in competition with FriendOS?

This question has popped up a couple of times the past two weeks. So I want to address that head on.

I make a product called Smart Mobile Studio. I do that with a group of well-known developers, and we have done so for many years now. The preliminary ideas were presented on my blog during the winter 2009, early 2010 and we started working (and blogging) after that. Smart Mobile Studio and it’s language, Smart Pascal (see Wikipedia article), takes object pascal (like freepascal or Delphi) and compiles to JavaScript rather than machine code.

smsamiga

The Smart compiler is due for OS4 once i get the A1222 in my hands

One of the examples that has shipped with Smart Mobile Studio, and also been available through a library called QTX, is something called Quartex Media Desktop. Which is an example of a NAS server front-end, a kiosk front-end (ticket ordering, cash machines etc) or just an intranet desktop where you centralize media and files. It is also node.js powered to deal with the back-end filesystem. This is now called amibian.js.

In other words – it has nothing to do with Friend software labs at all. In fact, I didn’t even know Friend existed until they approached me a few weeks ago.

15590846_10154070198815906_4207939673564686511_o

The Quartex Media desktop has been around for ages

Amibian.js is just an update of the Quartex Media Desktop example. It is not a commercial venture at all, but an example of how productive you can be with Smart pascal.

And it’s just one example out of more than a hundred that showcase different aspects of our run-time library. This example has been available since version 1.2 or 1.3 of Smart, so no, this is not me trying to reverse engineer FriendOS. Because I was doing this long before FriendOS even was presented. I have just added a windowing manager and made it look like OS4, which also happened before I had any contact with my buddies over at Friend Software Labs (why do you think they were interested in me).

16805402_1914020898827741_149245853_o

Early 2017 Linux bootloader by Gunnar

So, am I in competition with Friend? NO! I have absolutely no ambition, aspiration or intent for anything of the sorts. And should you be in doubt then let me break it down for you:

  • Hogne, Arne, David, Thomas, Francois and everyone at Friend Software Labs are friends of mine. I talk almost daily with David Pleasence who is a wonderful person and an inspiration for everyone who knows him.
  • Normal people don’t sneak around stabbing friends in the back. Plain and simple. That is not how I was raised, and such behavior is completely unacceptable.
  • Amibian.js is 110% pure Amiga oriented. The core of it has been a part of Smart for years now, and it has been freely available for anyone on google code and github.
  • For every change we have made to the Smart RTL, the media desktop example has been updated to reflect this. But ultimately it’s just one out of countless examples. We also have a working Facebook clone – but we’re not going into competition with Mark Zuckerberg for that matter.
  • People can invent the same things at the same time. Thats how reality works. There is a natural evolution of ideas, and great minds often think alike.

Why did you call it Amibian.js, it’s so confusing?

Well it’s a long story but to make it short. The first “boot into uae” thing was initially outlined by me (with the help of chips, the UAE4Arm maintainer). But I didn’t do it right because Linux has never really been my thing. So I just posted it on my retro-gaming blog and forgot all about it.

Gunnar picked this up and perfected it. He has worked weeks and months making Amibian into what it is today – together with Thomas, our spanish superhero /slash/ part-time dictator /slash/ minister of propaganda 🙂

We then started talking about making a new system. Not a new UAE, but something new and ground breaking. I proposed Smart Pascal, and we wondered how the Raspberry PI would run JavaScript performance wise. I then spent a couple of hours adding the icon layout grid and the windowing manager to our existing media desktop – and then fired up some HTML5 demos. Gunnar tested them under Chrome on the Raspberry PI — and voila, Amibian.js was born.

amidesk

And that is all there is to it. No drama, no hidden agendas – and no conspiracy.

I should also add that I do not work at Friend Software Labs, but we have excellent communication and I’m sure we will combine our forces on more than one software title in the future.

On a personal note I have more than a few titles I would like to port to FriendOS. One of my best sellers of all time is an invoice and credit application – which will be re-written in Smart Pascal (its presently a mix of Delphi and C++ builder code). The same program is also due to Amiga OS 4.1 whenever I get my A1222 (looking at you Trevor *smile*).

Well, I hope that clears up any misunderstanding regarding these very separate but superficially related topics. Amibian.js will remain 100% Amiga focused – that has been and remains our goal.

Ode to our childhood

Amibian is and will always be, an ode to the people who gave us such a great childhood. People like David Pleasence who was the face of Commodore in europe. A man who embody the friendliness of the Amiga with his very being. Probably one of the warmest and kindest people I can think of.

Francois Lionet, author of Amos Basic. The man who made me a programmer and that I cannot thank enough. And I know I’m not alone about learning from him.

Mark Sibly, the author of BlitzBasic, the man who taught me all those assembler tricks. A man that deserves to go down in the history books as one of the best programmers in history.

And above all – the people who made the Amiga itself; giants like Jay Miner, Dave Haynie, Carl Sassenrath, Dave Needle, RJ Michal (forgive me for not listing all of you. Your contributions will never be forgotten).

That is what Amibian.js is all about.

Patents and greed may have killed the actual code. But we are free to implement whatever we like from scratch. And when I’m done – your patents will be worthless..

 

Amiga revival, Smart Pascal and growing up

March 11, 2017 1 comment

Maybe its just me but the Amiga is kinda having a revival these days? Seems to me like the number of people going back to the Amiga has just exploded the past couple of years. Much of that is no doubt thanks to my buddy Gunnar Kristjannsen’s excellent work on the Amibian distro for Raspberry PI. Making a high-end Amiga experience that would have cost you thousands of dollars available at around $35.

amifuture

Looking forward to some cosy reading

While Gunnar’s great distro is no doubt a huge factor in this, I believe its more than just easy access. I think a lot of us that grew up with the system, who lived the Amiga daily from elementary school all the way to college – have come full circle. We spend our days coding on PC’s, Mac’s or making mobile software – but deep down inside, I think all of us are still in love with that magical machine; The Commodore Amiga.

I am honestly at a loss for words on this (and that’s a first, most days you can’t get me to shut the hell up). Why should a 30-year-old system attract me more, and still cause so much joy in my life – compared to the latest stuff? I mean, I got a fat ass I7 that growls when you start it with 64 gigabyte ram, SSD and all the extras; I got macs all over the house, the latest consoles – and enough embedded boards to start my own arcade if I so desired.

Yet at the end of the day, when the kids are in bed and GF firmly planted in front of her favorite tv show, fathers are down in basements all around europe. Not watching porn, not practising black magic or trying to transform led into gold, nope: coding in assembler on a mc68k processor running at a whopping 7Mhz and loving every minute of it!

Today the madness held no bounds and forced me, out of sheer perverted joy, to order 4 copies of Amiga Future magazine (yes there are still magazines for the Amiga, believe it or not), a few posters, a mousemat and (drumroll) the ever sexy A1222. Actually that was a lie, I ordered that weeks ago, Trevor Dickenson over at A-EON hooked me up so im getting it as soon as it comes off the assembly line. And for those that don’t know, the A1222 is the new affordable Amiga that is released today. It’s not a remake of the older models, but a brand new thing. I havent been this giddy about a piece of silicon since I fell into a double-d cup at a beach in Spain last year.

Smart Pascal

It made sense to unite my two great computing passions, namely the object pascal language and Amiga into one package. So whenever I have some spare time I work my ass off on the update for Smart Mobile Studio. And it’s getting probably the biggest “demo” ever shipped with a programming language.

What? Well, a remake of the Amiga operating system. But not just a simple css-styled shallow lookalike. You know me, I just had to go all the way. So I married the system with something called uae.js. Which is essentially the JavaScript version of the Amiga emulator. Its compiled with EmScripten – a post processor that takes LLVM compiled bitcode compiled with C/C++ and spits out Asm.js optimized code.

amidesk

You just cant kill it, Amiga is 4ever

So, Smart Pascal in one hand – C/C++ in the right hand. Its like being back in college all over again. Only thing missing now is that Wacom suddenly returns and Borland rise from the grave with another Turbo product. But yes, JavaScript is something I really enjoy. And being able to compile object pascal to JavaScript is even better.

The end result? Well since I don’t have too much time on my hands it’s roughly 31-32% done, and when we hit 50% is when UAE.js will be activated. So right now its a sexy cloud front end. It has a virtual filesystem that runs fine over localstorage, but it can also talk to node.js and access the real filesystem on your server.

But when UAE.js kicks in you will be able to run your favorite Amiga demos, applications and games in your browser. I am actually very excited about seeing the performance. It runs most demos OK (using the Aros rom-files). I imagine running things like blitzbasic, Amos basic and SAS-C/C++ should work fine. Or at least be within the “usable” range if you got a powerful PC to play with.

The V8 JavaScript engine in webkit is due for an overhaul next year – and while I can only speculate I’m guessing real-life compilation will be the addition. They already do some heavy JIT’ing but once you throw LLVM based actual compilation into the picture – large JS applications is going to fly side by side with native stuff. And that’s when cloud front-ends like ChromeOS and other FriendOS is going to take off.

My little remake is not that ambitious, but I do intend to make this an absolute kick-ass system as far as Amiga is concerned. And for Smart Pascal developers? Well, lets just say that this demo project has pushed the RTL for all it’s worth and helped fix bugs and expand the RTL in a way that makes it a real power-house!

Growing up

Do we ever really grow up? I’m not sure any more. I look at others and see some that have adopted this role, this image of how an adult should be like — but its more often than not tied into the whole A4 family thing or some superficial work profile. And since most Amiga fanatics are in their 40’s and 50’s (same age as Delphi hooligans, Turbo was released in 1983 same year as the Amiga came out), I guess this is when kids have grown up enough for people to go “wait a minute, what .. where is my Amiga!“.

But good things come to those who wait. If someone told me that I would one day work side by side with giants like David John Pleasance, Francois Lionet and the crew at FriendUp systems – I would never have believed them. A member of quartex in meetings with the head of Commodore? My teenage self would never have believed it. Both of these men, including all the tech guys at Commodore, Mark Sibly the guy behind BlitzBasic — these were my teenage heroes. And now I get to work with two of them. That is priceless.

As for growing up – if that means losing that spark, that trigger that when lost would render us incapable of enjoying things like the Amiga, reduced to a suit in a grey world of PCs – you know, then I’m happy to be exactly where I am. If you can go to work wearing an Amiga T-Shirt, tracker music on your iPod, a family you love at home, cool people to work with – I would call that a wrap.

And looking at the hundreds and thousands of people returning to the Amiga after 30 years in the desert – something tells me I wont be alone .. 😉

 

The Smart Desktop

March 5, 2017 Leave a comment

Right, if you checked out my previous post you know that I have been working on a cloud desktop for a while. It’s basiclly an example that will ship with the next release of Smart Mobile Studio, but it’s also a labour of love since I’m a huge Amiga fan.

My good friend Gunnar Kristjansson, the author behind Amibian, the Linux distro that more or less turns your Raspberry PI3b into a real live Amiga, booting straight into UAE4All on ARM based devices – has been helping out with testing and general opinions as well. We have decided to fork the base and call that “Amibian.js” since it is capable of running real-life 68k Amiga software right in the browser (!)

screenshot

Anyways if you find this interesting head over to github and pull the source from my smart repository. You will need the NG version of the RTL to compile, but that should be in the Alpha channel next week (unless something needs more love before its released).

Why should this matter?

The cloud desktop is not trying to be something it’s not. It’s meant as a conceptual example, meaning that it demonstrates what can be achieved (and expanded upon) with Smart Mobile Studio.

Having said that, it’s not just a pretty face! Making sexy css graphics is easy these days, but our RTL adds a bit of depth to the whole paradigm.

First of all, I implemented a virtual filesystem on top of Localstorage. This means that files can be read/written to the browser cache — and it will all be stored inside a single serialized item. This would be what people call “ram disk”. Essentially the capacity to store files in ram.

screenshot2

The same filesystem, which is naturally based on a common ancestor class, talks to the node.js backend server. Which means that once the server is finished – the desktop will have access to a region of the actual harddisk. This is where the fun starts!

What about applications

The desktop shows 3 windows when you start it. The first is a fully working text-editor. The second is a Deluxe Paint clone (pixel based editor) and last but not least – a music tracker (player) application.

Now all of these are pure JavaScript. Some of them are hosted elsewhere. So this is just an example of how easy it is to bind together local and external services.

Lets pixel like its 1999!

Lets pixel like its 1999!

I will naturally, when time allows, isolate these locally as a part of the repository – so that the applications can start as normal. They need to be adapted, because right now they run inside their own IFrame – which cause problems with window focus (as you will notice).

What can I use it for?

It should be a fairly good starting point if you want to create your own windowing cloud applications. I have given you the basics, like windowing classes, preferences and such things. The code is easy to read and play with – so it should be a good place to begin.

Personally I use this in my NAS developments. I discovered that my Asustor NAS (which has a similar concept) is a bit shit – so I decided I could write a better one myself.

Thankfully Gunnar Kristjannson, the guy behind Amibian, is helping me out with the Linux distro – and it boots directly into this system (if you have a screen attached). You can also connect to the NAS via ordinary http. And from that interface I can now control what the NAS does. And its all thanks to node.js and SMS.

The cool part? Replace the NAS with your favorite cloud supplier and you get the idea 😉

 

Smart Pascal: A real life desktop

February 16, 2017 6 comments

Every now and then I get feedback like “can SMS use jQuery?” or “Why don’t you use Sencha’s widgets“. And it just continues with everyone having their own favorite framework that they want SMS to adopt.

First of all, this is to completely misunderstand the architecture of Smart. You can use any framework you like. But you also have to sit down and write some code to incorporate it into the VJL. And believe me it’s not that hard.

The workbench

One of the cooler demo’s that I have been working on for .. oh, 8 hours now, is more or less a full Amiga OS 4 desktop clone. Sounds useless right? Well not really. It demonstrates some fundamental concepts:

  • How to create windows just like eh, windows have
  • How to display file-items in a listview
  • How to create controls outside the form so they remain unaffected by form switching
  • How to host demos and pre-compiled applications inside forms

But why stop there? Why not setup a node.js server and have the desktop act as a front-end to your server?

Always fun to see how far we can push the system

Check out this video on youtube 🙂

NAS front end

If you go out and buy a NAS today, chances are it comes with an HTML interface. So once you plug it into your router, you can browse to it and control the device via the browser.

Ring a bell? That is exactly what I’m doing right here. And it took me less than one working day to get this up and running. Here is how it looks right now:

Running a full remake of a classic demo in a window. No problem. You can run as much code as memory can hold

Running a full remake of a classic demo in a window. No problem. You can run as much code as memory can hold

But I want widget set x, y, z!

Had I done you the disservice of using Sencha or jQueryUI or whatever widget framework, you would have been stuck with that forever. Instead, you get a VCL like framework that is build in a way that ensures – that it can absorb and integrate any UI.

But let’s get back to that desktop.

You probably think: Ok so you have a fake desktop in a browser, it can run some JS demos and look cool. But so what?

You don’t get it. Did you know that X, the display system on Linux is by client / server system by default? Did you know that the entire Linux desktop is just an X client that connects to the server (the server being in the same distro) in order to do it’s business? If you start looking at what you can do – as opposed to what you imagine is impossible, there is a lot of cool stuff you can do for your company right now.

Doing some GPU profiling and watching the callstack

Doing some GPU profiling and watching the callstack

The first thing you need is to set up a server. A node.js server of course, that the website can talk to. Heck you can even do OAuth2 calls to dropbox and whatever online service you like – and get that on your HTML5 desktop without node.

Node however allows you to get system-level access. Listing files, loading files, saving files and even loading programs. Programs here being compiles Smart applications that the desktop can inject and execute inside a “window”.

Whenever you need something executing on the server – well then you call the server via websocket.

If node.js doesn’t do it for you then write it in Delphi or C++, it doesnt really matter. What matters is that you have a universal access point in the browser.

A quick visit to npm and git and you can download fully functional text-processors and large-scale HTML5 applications that does exactly what OpenOffice does. And with that websocket connection to your back-end, you have a real-life solution on your hands.

Here is a quick and dirty storage device API I made. Ram-disk will just store data to a B-Tree based “fake” in-memory filesystem. The real deal will come via websocket on the server.

unit Wb.desktop.Devices;

interface

uses
  System.Types, System.Types.Convert,
  System.Streams, System.Reader, System.Writer,
  System.Stream.Reader, System.Stream.Writer,
  System.Time,
  SmartCL.System,
  SmartCL.Time;

type

  TWbStorageDevice        = class;
  TWbStorageDeviceRamDisk = class;
  TWbDeviceManager        = class;
  TWbCustomFileSystem     = class;
  TWbVirtualFileSystem    = class;
  TWbRemoteFileSystem     = class;
  TWbLocalFileSystem      = class;
  TWbStorageDeviceClass = class of TWbStorageDevice;

  TWbCustomFileSystem = class(TObject)
  end;

  TWbVirtualFileSystem = class(TWbCustomFileSystem)
  end;

  TWbRemoteFileSystem = class(TWbCustomFileSystem)
  end;

  TWbLocalFileSystem = class(TWbCustomFileSystem)
  end;

  /* Requirements for using a device */
  TWbStorageDeviceOptions = set of
    (
      doRequireLogin, // Require authentication before Mount()
      doReadOnly      // Device is read-only
    );

  /* Filesystem access rights */
  TWbStorageDeviceAccess  = set of
    (
      daNone,       // none
      daReadOnly,   // read files only
      daReadWrite,  // read and write [create]
      daExecute     // can execute
    );

  TWbAuthenticatedEvent = procedure (Sender: TWbStorageDevice; Access: TWbStorageDeviceAccess);
  TWbMountEvent = procedure (Sender: TWbStorageDevice);

  /* Abstract storage device */
  TWbStorageDevice = class(TObject)
  private
    FId:            string;
    FName:          string;
    FFileSystem:    TWbCustomFileSystem;
    FOptions:       TWbStorageDeviceOptions;
    FMounted:       boolean;
    FAuthenticated: boolean;
    FManager:       TWbDeviceManager;
  protected
    procedure   SetName(const NewName: string); virtual;
    procedure   SetIdentifier(const NewId: string); virtual;
    procedure   SetFileSystem(const NewFileSystem: TWbCustomFileSystem); virtual;
    function    GetFileSystem: TWbCustomFileSystem; virtual;
    procedure   SetOptions(const NewOptions: TWbStorageDeviceOptions); virtual;
    function    GetOptions: TWbStorageDeviceOptions; virtual;
    procedure   SetAuthenticated(const NewState: boolean); virtual;
  public
    property    Name: string read FName;
    property    Identifier: string read FId;
    property    FileSystem: TWbCustomFileSystem read GetFileSystem;
    property    Options: TWbStorageDeviceOptions read GetOptions;
    property    Mounted: boolean read FMounted;
    property    Authenticated: boolean read FAuthenticated;
    property    DeviceManager: TWbDeviceManager read FManager;

    procedure   Authenticate(UserName, Password: string; const Success: TWbAuthenticatedEvent); overload;
    procedure   Authenticate(UserName, Password, Domain: string; Success: TWbAuthenticatedEvent); overload;
    procedure   Authenticate(AuthKey: string; Success: TWbAuthenticatedEvent); overload;

    procedure   Mount(const Success: TWbMountEvent);
    procedure   UnMount;

    constructor Create(const Manager: TWbDeviceManager); virtual;
    destructor  Destroy; override;
  end;

  /* RAM DISK */
  TWbStorageDeviceRamDisk = class(TWbStorageDevice)
  protected
    function    GetFileSystem: TWbCustomFileSystem; override;
  public
    constructor Create(const Manager: TWbDeviceManager); override;
  end;

  /* Cache disk */
  TWbStorageDeviceCache = class(TWbStorageDevice)
  protected
    function    GetFileSystem: TWbCustomFileSystem; override;
  public
    constructor Create(const Manager: TWbDeviceManager); override;
  end;

  TWbDeviceManager = class(TObject)
  private
    FClasses: array of TWbStorageDeviceClass;
    FObjects: array of TWbStorageDevice;
  public
    procedure RegisterDevice(const DeviceClass: TWbStorageDeviceClass);

    property  Count: integer read ( FObjects.Count );
    property  Device[const Index: integer]: TWbStorageDevice read ( FObjects[Index] ); default;

    destructor Destroy; override;
  end;

implementation

//#############################################################################
// TWbDeviceManager
//#############################################################################

destructor TWbDeviceManager.Destroy;
begin
  while FObjects.Count >0 do
  begin
    FObjects[0].free;
    FObjects.Delete(0,1);
  end;
  FClasses.Clear();
  inherited;
end;

procedure TWbDeviceManager.RegisterDevice(const DeviceClass: TWbStorageDeviceClass);
begin
  if FClasses.IndexOf(DeviceClass) < 0 then
  begin
    FClasses.add(DeviceClass);
    FObjects.add( DeviceClass.Create(self) );
  end;
end;

//#############################################################################
// TWbStorageDeviceCache
//#############################################################################

constructor TWbStorageDeviceCache.Create(const Manager: TWbDeviceManager);
begin
  inherited Create(Manager);
  SetName('DH0');
  SetIdentifier('{2D58F4D9-D8FE-434C-AC32-8B27EEC0AEE2}');
  SetOptions([doReadOnly]);
end;

function TWbStorageDeviceCache.GetFileSystem: TWbCustomFileSystem;
begin
  result := inherited GetFileSystem();
  if result = nil then
  begin
    result := TWbVirtualFileSystem.Create;
    SetFileSystem(result);
  end;
end;

//#############################################################################
// TWbRamDisk
//#############################################################################

constructor TWbStorageDeviceRamDisk.Create(const Manager: TWbDeviceManager);
begin
  inherited Create(Manager);
  SetName('Ram-Disk');
  SetIdentifier('{2E6D58D0-A0C3-4D62-8AC4-0300619418A6}');
  SetOptions([]);
end;

function TWbStorageDeviceRamDisk.GetFileSystem: TWbCustomFileSystem;
begin
  result := inherited GetFileSystem();
  if result = nil then
  begin
    result := TWbVirtualFileSystem.Create;
    SetFileSystem(result);
  end;
end;

//#############################################################################
// TWbStorageDevice
//#############################################################################

constructor TWbStorageDevice.Create(const Manager: TWbDeviceManager);
begin
  inherited Create;
  FManager := Manager;
end;

destructor TWbStorageDevice.Destroy;
begin
  if FFileSystem <> nil then
    FFileSystem.free;
  inherited;
end;

procedure TWbStorageDevice.Mount(const Success: TWbMountEvent);
begin
  if FMounted then
    UnMount;

  FMounted := true;

  if assigned(Success) then
  begin
    TW3Dispatch.Execute( procedure ()
      begin
        Success(self);
      end, 100);
  end;
end;

procedure TWbStorageDevice.UnMount;
begin
  if FMounted then
  begin
    FMounted := false;
  end;
end;

procedure TWbStorageDevice.SetAuthenticated(const NewState: boolean);
begin
  FAuthenticated := NewState;
end;

procedure TWbStorageDevice.SetOptions(const NewOptions: TWbStorageDeviceOptions);
begin
  FOptions := NewOptions;
end;

function TWbStorageDevice.GetOptions: TWbStorageDeviceOptions;
begin
  result := FOptions;
end;

procedure TWbStorageDevice.SetName(const NewName: string);
begin
  FName := NewName;
end;

procedure TWbStorageDevice.SetIdentifier(const NewId: string);
begin
  FId := NewId;
end;

procedure TWbStorageDevice.SetFileSystem(const NewFileSystem: TWbCustomFileSystem);
begin
  FFileSystem := NewFileSystem;
end;

function TWbStorageDevice.GetFileSystem: TWbCustomFileSystem;
begin
  result := FFileSystem;
end;

procedure TWbStorageDevice.Authenticate(UserName, Password: string; const Success: TWbAuthenticatedEvent);
begin
end;

procedure TWbStorageDevice.Authenticate(UserName, Password, Domain: string; Success: TWbAuthenticatedEvent);
begin
end;

procedure TWbStorageDevice.Authenticate(AuthKey: string; Success: TWbAuthenticatedEvent);
begin
end;

end.

So. Writing the foundation of a NAS front-end in Smart, a virtual desktop with a windowing toolkit took me less than 8 hours. How long would it take you in vanilla JS?

So forgive me if I dont take jQuery serious.

20% discount on HexLicense!

February 10, 2017 Leave a comment

For a short time Quartex Components offer you the FMX, VCL and VJL package with a whopping $40 discount! That is a significant saving for a great product!

By acting now you gain full access to the classical component packages – as well as the next-generation licensing engine and platform:

Ironwood now supports Smart Pascal! The Delphi edition is just around the corner. Start using Hexlicense in Delphi today!

Ironwood now supports Smart Pascal! The Delphi update is just around the corner. Start using Hexlicense in Delphi today!

  • 12 month subscription
    • 4 updates guaranteed
  • Full source code
    • VCL version
    • FMX version
      • Windows
      • OS X
      • iOS
      • Android
    • JVL version (Smart)
      • All mobile platforms
      • HTML5 applications
      • Node.js client and server applications
  • Solid documentation
  • Easy to use and drop into existing projects
  • Ships with examples
  • Support via E-mail

Ironwood

ironwood2The next generation HexLicense formula and license generator is nicknamed “Ironwood”. This has been in the making for some time and is through the beta phase. It uses the absolute latest RTL for Smart – which will be in circulation within a 1 to 2 weeks (hopefully sooner!). So you are getting the absolute latest to play with – which is can be used by both visual and node.js projects.

By acting now you not only save money but you also get a great deal on our classical Delphi components. Most importantly however, is that the discount buys you access to the next generation components for Delphi as well. These will retail at a higher price when they hit the market.

Smart Pascal

With mobile application development taking place more and more through HTML5 and phonegap – not to mention that node.js is quickly becoming a market standard server-side, compilers that targets the JavaScript virtual machine is becoming increasingly important. Especially for traditional languages like Delphi and C++.

With access to Ironwood for Delphi and Smart Pascal, your existing VCL and FMX products can continue to function as they do right now – while you move your licensing mechanisms to the cost-effective, scalable and portable node.js platform.

Why pay huge amounts of money to rent a full virtual instance to host a native Delphi service – when you can do the exact same under node.js for but a fraction of the price? Not to mention the many benefits node brings to the table.

Discount covers all platforms!

discountThe offer gives you the entire system, including VCL, FMX and JVL editions. You also secure access to Ironwood for Delphi.

Again, this package will retail at a higher price and forms the basis of our future cloud based licensing services.

Hexlicense for Delphi can be dropped directly into existing projects, comes with a license generator application and is considered very easy to use.

By acting now you secure early access!

Buy Now Button with Credit Cards

Note: This is a time limited offer. Only the link above this text is valid for this discount.

To read more about HexLicense, head over to the website. You can also download the documentation which is substancial and covers everything.

Smart Pascal: Changes

February 6, 2017 3 comments

The changes to Smart Mobile Studio over the past 12 months have been tremendous. At first glance they might not seem huge, but if you do a raw compare on the upcoming RTL and the now more than a year old RTL – I think you will find that there are very few places in the code where I havent done improvements.

In this post I will try to collect some of the changes. A final change log will be released together with the update, but at least this “preview” will tell you something about the hours, days, weeks and months I have put into the RTL.

The Smart Lab, this is where most of my ideas turn into code :)

The Smart Lab, this is where most of my ideas turn into code 🙂

There will also be changes to the IDE; We have 3 members that both have added new features in the past, and members that are adding changes right now. The immediate being an update from Chromium Embedded CEF2 to CEF4, which is a huge change in speed and preview quality. And there are other more important changes being worked on, the most pressing being the designer and “live” rendering of controls.

Ok, let’s just dive into it!

RTL Changes

  • The units that make up the RTL are now organized according to a namespace scheme
    • Visual units are prefixed with SmartCL
    • Universal units are prefixed with System
    • Low level API units for node.js are prefixed with NodeJS
    • High level Node classes are prefixed with SmartNJ
  • Better fragmentation: Code that was previously a part of one very large unit has been divided into smaller files to avoid large binaries. For example:
    •  TRect, TPoint etc. are now in System.Types.Graphics
    • System.Time is extended with SmartCL.Time which gives graphical timing functions like TW3Dispatch.RequestAnimationFrame()
  • The unit System.Objects has been added, isolating classes that work both in browsers, node and headless runtime environments:
    • TW3ErrorObjectOptions
    • TW3ErrorObject
    • TW3OwnedErrorObject
    • TW3HandleBasedObject
  • TW3Label has been completely re-written and is now faster, has synchronized state management, resize on ready-state and much more
  • The unit System.NameValuePairs was added. This implements a traditional name/value list (or dictionary) that is used by various classes and standards throughout the RTL (http headers being an example)
  • A show stopping bug in SmartCL.Effects has been fixed. Effects now works as expected (simply add SmartCL.Effects to your uses clause)
  • TFileStream has been added to SmartNJ.Streams. This is unique for node.js since only node (and some hybrid runtime engines) support direct file access.
  • TBinaryData is extended in SmartNJ.Streams.pas to emit and consume node.js buffers which are different from traditional JS buffers. ToNodeBuffer() and FromNodeBuffer() allows you to consume special node buffer types.
  • A full software tweening engine has been written from scratch in pure Smart Pascal and added to the SmartCL namespace. The following units have been added:
    • SmartCL.Tween
      • TW3TweenElement
      • TW3TweenEngine
      • function TweenEngine: TW3TweenEngine
    • SmartCL.Tween.Effect
      • TW3CustomEffect
      • TW3CustomTweenEffect
      • TW3ControlTweenEffect
      • TW3MoveXEffect
      • TW3MoveYEffect
      • TW3MoveToEffect
      • TW3ColorMorphEffect
      • TW3CustomOpacityEffect
      • TW3FadeInEffect
      • TW3FadeOutEffect
      • TW3OpacityEffect
    • SmartCL.Tween.Ease
      • TW3TweenEase
      • TW3TweenEaseLinear
      • TW3TweenEaseQuadIn
      • TW3TweenEaseQuadOut
      • TW3TweenEaseQuadInOut
      • TW3TweenEaseCubeIn
      • TW3TweenEaseCubeOut
      • TW3TweenEaseCubeInOut
      • TW3TweenEaseQuartIn
      • TW3TweenEaseQuartOut
      • TW3TweenEaseQuartInOut
      • TW3TweenEaseQuintIn
      • TW3TweenEaseQuintOut
      • TW3TweenEaseQuintInOut
      • TW3TweenEaseSineIn
      • TW3TweenEaseSineOut
      • TW3TweenEaseSineInOut
      • TW3TweenEaseExpoIn
      • TW3TweenEaseExpoOut
      • TW3TweenEaseExpoInOut
      • TW3TweenEaseCollection
      • function TweenEaseCollection: TW3TweenEaseCollection
  • Support for require.js (simplified resource management and more) is now a part of the RTL. The unit SmartCL.Require gives you:
    • TRequireError
    • TW3RequireJSConfig
    • TW3RequireJS
    • function Require: TW3RequireJS;
    • procedure Require(Files: TStrArray);
    • procedure Require(Files: TStrArray; const Success: TProcedureRef);
    • procedure Require(Files: TStrArray; const Success: TProcedureRef; const Failure: TW3RequireErrHandler);
  • SmartCL (browser) based communication has been consolidated into more appropriate named units:
    • SmartCL.Net.http
    • SmartCL.Net.http.headers
    • SmartCL.Net.websocket
    • SmartCL.Net.jsonp
    • SmartCL.Net.socketIO
    • SmartCL.Net.rest
  • A callback bug in the REST api has been fixed (called wrong handler on exit)
  • Suppport for mutation events has been added to the RTL. This can be found in the unit SmartCL.Observer. Mutation observer allows you to listen for changes to any HTML element, its properties or attributes. This is a very important unit with regards to data-aware controls:
    • TMutationObserverOptions
    • TMutationObserver
  • A fallback shim for mutation observing has been added to the $RTL\Shims folder. This ensures that mutation events can be observed even on older browsers.
  • A MSIE legacy shim that patches all missing IE functionality from the current back to IE5 has been added. This allows Smart applications to execute without problems on older browsers (which there are surpricingly many of)
  • The unit SmartCL.Styles has been added. This contains classes and methods that create a stylesheet at runtime. The stylesheet is deleted when the object instance is disposed. This allows for easier styling from within your code rather than having to pre-define it in the global css file.
  • Polygon helper classes have been isolated in SmartCL.Polygons. These expose functionality to TPointArray and TPointFArray:
    • TPolygonHelper
    • TPolygonFHelper
  • Support for socket.io has been added for the browser. The unit SmartCL.Net.SocketIO contains the following client class and methods:
    • TW3SocketIOClient
      • procedure Connect(RemoteHost: string)
      • procedure Disconnect
      • procedure Emit(EventName: string; Data: variant)
      • procedure On(EventName: string; Handler: TW3SocketIOHandler)
  • Support for socket.io has been added to the new SmartNJ namespace as well, allowing you to write fast, good performance SocketIO servers.
  • Support for WebSocketIO, an amalgamation of classic WebSocket and SocketIO has been added. This is the default socketio server type for our node.js system
  • Support for user-attributes has been added and isolated in SmartCL.Attributes. This allows you to easily add, read, write and remove custom attributes to any tag. This is a very important feature that is used both by the effects framework – and also by the database framework.
  • SmartCl.Buffers unit has been updated. Now uses the latest methods of the RTL and now works on all browsers, not just webkit and Firefox.
  • $RTL\SmartCL\Controllers\SmartCL.Scroll.Momentum has been removed
  • $RTL\SmartCL\Controllers\SmartCL.Scroll.plain has been removed
  • All visual controls have been updated, rewritten and thoroughly tested
  • The RTL now has support for ACE, the #1 JavaScript code editor. This is more or less a JavaScript implementation of Delphi’s Synedit, but with functionality closer to Sublime. Ace comprises over 100 files. In this first release we have focused on getting a respectable basis in place.
    • The wrapper units is SmartCL.AceEditor, which gives you the following classes:
      • TW3AceMode
      • TW3AceTheme
      • TW3AceEditor
        • Text: string
        • SelectedText: string
        • LineCount: integer
        • WordWrap: boolean {get; set;}
        • ShowPrintMargin: boolean {get; set;}
        • EditorMode: TW3AceMode {get; set;}
        • Theme: TW3AceTheme {get; set;}
    • Modes and syntax we added support for:
      • AceEdit.Mode.Abap
      • AceEdit.Mode.Abc
      • AceEdit.Mode.ActionScript
      • AceEdit.Mode.Ada
      • AceEdit.Mode.Apache
      • AceEdit.Mode.AppleScript
      • AceEdit.Mode.Ascii
      • AceEdit.Mode.AutoHotKey
      • AceEdit.Mode.Base
      • AceEdit.Mode.BatchFile
      • AceEdit.Mode.C9Search
      • AceEdit.Mode.Cirru
      • AceEdit.Mode.Clojure
      • AceEdit.Mode.Cobol
      • AceEdit.Mode.Coffee
      • AceEdit.Mode.ColdFusion
      • AceEdit.Mode.Cpp
      • AceEdit.Mode.CSharp
      • AceEdit.Mode.CSS
      • AceEdit.Mode.Curly
      • AceEdit.Mode.D
      • AceEdit.Mode.Dart
      • AceEdit.Mode.Diff
      • AceEdit.Mode.django
      • AceEdit.Mode.Pascal
      • AceEdit.Mode.VbScript
      • AceEdit.Mode.X86Asm
    • Themes we support:
      • AceEdit.Theme.Ambiance
      • AceEdit.Theme.Base
      • AceEdit.Theme.Chaos
      • AceEdit.Theme.Chrome
      • AceEdit.Theme.CloudMidnight
      • AceEdit.Theme.Clouds
      • AceEdit.Theme.Cobalt
      • AceEdit.Theme.CrimsonEdit
      • AceEdit.Theme.Dawn
      • AceEdit.Theme.Dreamweaver
      • AceEdit.Theme.eclipse
      • AceEdit.Theme.Github
      • AceEdit.Theme.Monokai
      • AceEdit.Theme.Twilight
  • The RTL has a completely re-written ready-state engine. Since JavaScript is ASync by nature, the handle or reference to the TAG a class manages may not be ready during the constructor. The ready-state engine waits in the background for the handle to become valid and the control available in the DOM (document object model). It then sets the appropriate component-state flags and issues a resize to ensure that the control looks as it should.
  • Support for Control-State (which is common in Delphi and Lazarus) has been added to visual controls. The following state flags can be read or set:
    • csCreating
    • csLoading
    • csReady
    • csSized
    • csMoved
    • csDestroying
  • TW3CustomControl now supports creation-flags. This is also a feature common to Delphi’s VCL and Lazarus’s LCL. It allows you to set some flags that disable fundamental behavior when you don’t need it. For instance, a label which will always have a fixed size or be in a fixed place does not need Resize() management. Turning off resize-checking makes you application execute faster. The following creation flags can be defined:
    • cfIgnoreReadyState
    • cfSupportAdjustment
    • cfReportChildAddition
    • cfReportChildRemoval
    • cfReportMovement
    • cfReportResize
    • cfAllowSelection
    • cfKeyCapture
  • To enable or disable component states, use the following methods of TW3CustomControl:
    • procedure AddToComponentState(const Flags: TComponentState)
    • procedure RemoveFromComponentState(Const Flags: TComponentState)
  • Support for keyboard input and charcode capture has been added to the RTL. Simply add the flag cfKeyCapture to the CreationFlags() function of your custom-control and the control’s OnKeyPress event will fire when the control has focus and a key is pressed. By default this is turned off.
  • Support for text-selection and disabling text-selection has been added. Simply add or remove cfAllowSelection from your controls CreationFlags() function. By default text selection is turned off (except for obvious controls like text-edit).
  • TW3CustomControl now has a zIndex property. This represents the z-order of a control (which control is in front of others).
  • The function TW3CustomControl.GetZOrderList(sort: boolean) can be called to get a sorted or un-sorted list of child elements. This provides both the handle for each control and its zindex value.
  • The method TW3CustomControl.Showing() has been updated, it now takes height for IFrame and CSS4 GPU positioning (read: it will understand that its offscreen even if you use CSS3 to move it).
  • TW3CustomControl now has a Cursor property. This allows you to read and set the mouse cursor for a control. The following cursors are supported:
    • crAuto
    • crDefault
    • crInherited
    • crURL
    • crCrossHair
    • crHelp
    • crMove
    • crPointer
    • crProgress
    • crText
    • crWait
    • crNResize
    • crSResize
    • crEResize
    • crWResize
    • crNEResize
    • crNWResize
    • crNSResize
    • crSEResize
    • crSWResize
    • crEWResize
  • To simplify mouse cursor handling, the class TW3MouseCursor, which is a static class, has been added to SmartCL.System.pas. It expose the following methods:
    • function  CursorByName(const CursorName: string): TCursor
    • function  NameByCursor(const Cursor: TCursor): String
    • function  GetCursorFromElement(const Handle: TControlHandle): TCursor
    • procedure SetCursorForElement (const Handle: TControlHandle; const Cursor: TCursor)
  • TW3CustomControl have two new methods to deal with cursor changes. These are virtual and can be overriden:
    •  function GetMouseCursor: TCursor;
    • procedure SetMouseCursor(const NewCursor: TCursor);
  • Basic device capabillity examination support has been added. The class TW3DOMDeviceCapabilities has been added to SmartCL.System. This exposes the following methods and properties:
    • DevicePixelRatio: float
    • DisplayPixelsPerInch: TPixelsPerInch
    • GetMouseSupport: boolean
    • GetTouchSupport: boolean
    • GetGamePadSupport: boolean
    • GetKeyboardSupported: boolean
    • GetDevicePixelRatio: float
    • GetDisplayPixelsPerInch: TPixelsPerInch
  • In order to make the VJL more architectually compatible with Delphi’s VCL and Freepascal’s LCL – TW3Component as name has been pushed back. TW3TagObj used to be the root class for visual components, followed by TW3Component, TW3MovableControl and finally TW3CustomControl. However, since we want to use TW3Component as a common non-visual control, this name was pushed back. So TW3TagObj now inherits from TW3Component, and TW3TagContainer has taken the place TW3Component once had. Here is the new inheritance chain
    • TW3CustomComponent
      • TW3Component
        • TW3TagObj
          • TW3TagContainer
            • TW3MovableControl
              • TW3GraphicControl
              • TW3CustomControl
  • TW3Component is now the basis for non-visual components, which opens up for a whole new set of controls that can be dragged onto a form or datamodule. Sadly Datamodules did not make it into this update, but it is the next logical step – hopefully we will have it in place with the IDE update that will appear after this.
    • Controllers will eventually be re-incarnated as TW3Component’s
    • Rouge classes will be implemented as TW3Components
    • Database classes will become non-visual TW3Components
    • Communication classes will become non-visual TW3Components
  • TW3Timer is moved to the unit System.Time.pas
  • TW3Timer now inherits from TW3Component
  • The following procedures have been deprecated and moved to System.Time.pas. Please use the corresponding methods in TW3Dispatch (Note: The w3_* methods will be deleted in the next update!):
    • w3_Callback = TW3Dispatch.Execute()
    • w3_SetInterval = TW3Dispatch.SetInterval()
    • w3_ClearInterval = TW3Dispatch.ClearInterval()
    • w3_SetTimeout = TW3Dispatch.SetTimeOut()
    • w3_ClearTimeout = TW3Dispatch.ClearTimeOut()
  • TW3Dispatch, which is a class dealing with timing and scheduling, now support the following new methods:
    • class function JsNow: JDate;
    • class function Ticks: integer;
    •  class function TicksOf(const Present: TDateTime): integer;
    • class function TicksBetween(const Past, Future: TDateTime): integer;
    • class procedure RepeatExecute(const Entrypoint: TProcedureRef; const RepeatCount: integer; const IntervalInMs: integer);
  • SQLite has been recompiled from C to JS and is now version 3.8.7.4
  • The SQLite units have been moved from SmartCL.SQLite to System.SQLite
  • The following SQLite classes have been given an overhaul:
    • TSQLiteDatabase
    • TSQLiteDBObject
    • TSQLiteRowValues
    • TSQLiteResult
    • TSQLParamData
    • TSQLitePair
    • TSQLiteParams
    • TSQLiteStatement
  • The SQLiteInitialize() global method is no longer requires and has been removed. When you include System.SQLite.pas in your project, the whole database engine is automatically linked and loaded into memory on execute. Use the SQLiteReady() global function to check if the database engine has loaded before use.
  • TReader, TWriter which was exclusively a re-implementation based on LCL and Delphi has been fragmented. TReader and TWriter are now base classes that takes an interface as parameter in their constructor (IBinaryTransport). Both TStream and TBinaryData, which are the easiest ways of dealing with binary files and memory – implements the IBinaryTransport interface.
  • TStreamReader and TStreamWriter has been added to the RTL, these are implemented in System.Stream.Reader.pas and System.Stream.Writer.pas. When working with streams, make sure you use TStreamReader and TStreamWriter. Using TReader and TWriter directly may cause problems if you are not intimately familiar with the RTL.
  • The unit System.Structure has been added to the RTL. This is to simplify working with structured records, abstracting you from the underlying format. The following classes are exposed in System.Structure:
    • EW3Structure [exception]
    • TW3Structure
      • procedure WriteString(Name: string; Value: string; const Encode: boolean);
      • procedure WriteInt(const Name: string; value: integer);
      • procedure WriteBool(const Name: string; value: boolean);
      • procedure WriteFloat(const Name: string; value: float);
      • procedure WriteDateTime(const Name: string; value: TDateTime);
      • function ReadString(const Name: string): string;
      • function ReadInt(const Name: string): integer;
      • function ReadBool(const Name: string): boolean;
      • function ReadFloat(const Name: string): float;
      • function ReadDateTime(const Name: string): TDateTime;
      • function Read(const Name: string): variant;
      • procedure Write(const Name: string; const Value: variant);
      • procedure Clear;
      • procedure SaveToStream(const Stream: TStream);
      • procedure LoadFromStream(const Stream: TStream);
      • procedure LoadFromFile(Url: string; const callback: TW3StructureLoadedCallback);
  • The unit System.Structure.JSON implements a TW3Structure class that stores data as JSON. The data is stored in memory as a single record, but naturally you can add further records as child properties – and then export the complete data as binary format to a stream, or as a single string through the ToString() method.
  • The unit System.Structure.XML implements a TW3Structure class which emits XML. Since the XML interface in browsers is both unstable and wildly different between vendors, we use our own BTree engine to manage the data. The class emits valid XML and will also deal with sub-records just as TW3JSonStructure.
  • The unit System.JSON has been added, which contains the following classes and methods. TJSON is a highly effective wrapper over the built-in JSON API, making it easier to work with JSON in general. The constructor is highly overloaded so you can wrap existing JS elements directly. This class is used by TW3JSONStructure to simplify its work:
    • TJSONObjectOptions
    • EJSONObject [exception]
    • TJSONObject
    • TJSON
  • The Delphi parser library TextCraft has been added to the RTL. When I write added that is not really correct. TextCraft was first written in Smart and then post converted to Delphi. But the RTL now has the latest update of this library, making advanced, recursive text-parsing possible:
    • System.Text.Parser.pas
    • System.Text.Parser.Words.pas
  • TW3Borders has been re-coded and optimized.
    • TW3Border.EdgeString() now use a lookup table for maximum efficiency
    • TW3Border’s edges now use a lookup table to quickly map types to strings
    • All superfluous reference testing has been removed, resulting in much faster and efficient code
    • Styles are no longer read via the older w3_read/write mechanisms but rather directly from the controlhandle
  • Two new classes has been added to deal with border-radius.
    • All controls can set the radius for all edges using the older TW3MovableControl.BorderRadius property, but this has now been expanded on. The following classes has been added. TW3BorderRadius can be accessed via the new TW3CustomControl.EdgeRadius property, which creates an instance on demand (first access):
      • TW3BorderEdgeRadius
      • TW3BorderEdgeTopRadius
      • TW3BorderEdgeBottomRadius
      • TW3BorderRadius
  • TStringBuilder has been added to System.Type. It deviates some from the Lazarus and Delphi variations because it adds functions that actually makes sense ! 🙂
  • All custom controls have a cool background class that allows you to set color, add images and export pixmaps. A new class called TW3ControlBackgroundSize has been added to this (TW3CustomControl.Background.Size) making things even easier. Especially detecting if a background is a graphic and its true size (!)
    • function  BackgroundIsImage: boolean;
    • property Mode: TW3ControlBackgroundSizeMode
    • property Width: integer;
    • property Height: integer;
  • TW3Constraints (inherits from TW3OwnedObject) has been added to the RTL. This is a class that deals with size constraints for controls. Please note that this is experimental (!) When active it forces size restrictions much like the VCL and LCL — but it can cause havoc in a design that auto-scales (!). It implements the following:
    • property Enabled: boolean
    • property MinWidth: integer
    • property MinHeight: integer
    • property MaxWidth: integer
    • property MaxHeight: integer
    • procedure   ApplyToOwner
    • function GetMaxWidth: integer;
    • function GetMaxHeight: integer;
    • procedure SetMaxWidth(const NewMaxWidth: integer);
    • procedure SetMaxHeight(const NewMaxHeight: integer);
    • function GetMinWidth: integer;
    • function GetMinHeight: integer;
    • procedure SetMinWidth(aValue: integer);
    • procedure SetMinHeight(aValue: integer);
    • procedure   SetEnabled(const NewValue: boolean); virtual;
  • Event classes has finally been added! Need another OnClick event? just Create an event object and attach it to your control. The following event classes are now in SmartCL.Events :
    • TW3DOMEvent
    • TW3StandardDOMEvent
    • TW3MouseEnterEvent
    • TW3MouseLeaveEvent
    • TW3MouseDownEvent
    • TW3MouseMoveEvent
    • TW3MouseUpEvent
    • TW3ElementRemovedEvent
    • TW3ElementAddedEvent
    • TW3ElementContextMenuEvent
    • TW3ElementClickEvent
    • TW3ElementDblClickEvent
    • TW3ElementMouseOverEvent
    • TW3ElementKeyDownEvent
    • TW3ElementKeyPressEvent
    • TW3ElementKeyUpEvent
    • TW3ElementChangeEvent
    • TW3ElementMouseWheelEvent
    • TW3DOMEventAPI
      • class procedure RegisterEvent(Handle: TControlHandle; EventName: string; EventHandler:TW3JSEventHandler; Mode: TW3DOMEventMode); static;
      • class procedure UnRegisterEvent(Handle: TControlHandle; EventName: string; EventHandler:TW3JSEventHandler; Mode: TW3DOMEventMode); static;
  • Since events are not DOM or browser bound, they also occur in node.js and other JavaScript virtual machines (and you can also register your own events just like you do in Delphi or C#). As such I added System.Events.pas which contains the basic functionality for OOP events. The following classes and methods are added:
    • TW3SystemEventObject
      • procedure Attach(NameOfEvent: string);
      • procedure Detach;
      • property  Attached: boolean
      • property  EventName: string
  • TW3CustomBrowserAPI.Styles references “window.styles” which is no longer supported by browsers. This has been updated to “window.document.head.style”
  • BrowserAPI() now expose key browser objects as actual class objects. The following objects are exposed as handles only (as they have been for some time):
    • Document
    • Body
    • Window
    • Styles
    • Console
    • Navigator
    • Self
    • Event
  • Besides the above handles, we now expose direct access to the mapped class objects directly. The W3C units have been updated to reflect modern browsers (so the objects have the latest methods exposed):
    • DocumentObject: JDocument
    • BodyObject: JHTMLElement
    • WindowObject: JWindow
    • StylesObject: JCSSStyleDeclaration
    • NavigatorObject: JNavigator
    • EventObject: JEvent
  • Added missing W3C.localStorage unit file which expose the localstorage API. This can now be accessed via BrowserAPI().WindowObject.localStorage. It supports the following functions:
    • property  key[const keyId: string]: variant
    • property  length: integer
    • procedure setItem(const key: string; const Value: variant)
    • procedure removeItem(const key: string)
    • procedure clear
    • function  valueOf: variant [* prototype]
    • function  hasOwnProperty(const key: string): boolean [* prototype]
  • TW3Image now has a fitstyle property which wraps the “object-fit” css style. This gives you finer control over how the image is presented inside the control (or the background of a control). The following presentation styles are supported:
    •  fsNone
      Image will ignore the height and width of the parent and retain its original size.
    • fsFill
      This is the default value which stretches the image to fit the content box, regardless of its aspect-ratio.
    • fsContain
      Increases or decreases the size of the image to fill the box whilst preserving its aspect-ratio.
    • fsCover
      The image will fill the height and width of its box, once again maintaining its aspect ratio but often cropping the image in the process.
    • fsScaleDown
      The control will compare the difference between fsNone and fsContain in order to find the smallest concrete object size.
  • TW3Image now supports binary IO besides the common HTML src property. The following methods have been added to TW3Image:
    • function  ToBuffer: TBinaryData;
    • function  ToStream: TStream;
    • function  ToDataUrl: string;
    • function  ToImageData: TW3ImageData;
    • procedure LoadFromURL(aURL: String);
    • procedure LoadFromImageData(Const Data:TW3ImageData);
    • procedure LoadFromBinaryData(const Memory:TBinaryData);
    • procedure LoadFromStream(const Stream:TStream);
    • procedure Allocate(aWidth,aHeight:Integer);
  • TW3Image now expose 3 new properties that are very helpful. The PixelWidth and PixelHeight gives you the actual size of an image, regardless of scale or viewport proportions. This is handy when using the Allocate() method to create a pixel-buffer that matches an existing image:
    • property  PixelWidth: integer
    • property  PixelHeight: integer
    • property  Complete: boolean
  • The following procedures and functions have been removed. Faster to assign and deal with events directly on the handle – no need for the extra call:
    • procedure w3_bind
    • procedure w3_bind2
    • procedure w3_unbind
    • procedure w3_unbind2
  • TStreamHelper has been added to SmartCL.System. It adds data objectification. Generating an internal URL to access the data via a link. These functions wraps the createObjectURL API (https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL) The following methods are in the helper:
    • function  GetObjectURL: string;
    • procedure RevokeObjectURL(const ObjectUrl: string);
  • TAllocationHelper has been added to SmartCL.System. It does exactly the same as TStreamHelper but for the memory class TAllocation (system.memory.allocation.pas).
  • TBinaryData which is a very flexible and fast memory buffer class (system.memory.buffer.pas), inherits from TAllocation – as such TAllocationHelper also affects this class.
  • The class TW3URLObject, which is a static class, has been added to SmartCL.System.pas. It wraps the same API as TStreamHelper and TAllocationHelper, but operates independent of medium. This class also adds forced download methods – where you can start a “save as” of binary data (or any data) with a single call. The class interface contains the following:
    • class function  GetObjectURL(const Text, Encoding, ContentType, Charset: string): string;
    • class function  GetObjectURL(const Text: string): string;
    • class function  GetObjectURL(const Stream: TStream): string;
    • class function  GetObjectURL(const Data: TAllocation): string;
    • class procedure RevokeObjectURL(const ObjectUrl: string);
    • class procedure Download(const ObjectURL: string; Filename: string);
    • class procedure Download(const ObjectURL: string; Filename: string;
      const OnStarted: TProcedureRefId);
  • TW3CustomControl now have automatic ZIndex assignment. On creation a ZIndex value is automatically assigned to each control (auto-inc number). The following methods have been added for that value:
    • function  GetZIndex: integer;
    • procedure SetZIndex(const NewZIndex: integer);
  • To better deal with z-order and the creation sequence of child elements, the following methods have been added to TW3CustomControl. This makes it easier to control how child elements stack and overlap:
    • function  GetZOrderList(const Sort: boolean): TW3StackingOrderList;
    • function  GetChildrenSortedByYPos: TW3TagContainerArray;
    • function  GetChildrenSortedByXpos: TW3TagContainerArray;
  • TW3CustomControl’s Invalidate() method has previously done nothing. It has been present for TW3GraphicControl (which inherits from TW3CustomControl) as means of redrawing or refreshing graphics.
    Invalidate for non-graphic controls now calls resize() to update the layout. While coders should use BeginUpdate / EndUpdate, there are times when a call to Invalidate() is all you need.
  • Text-shadow, which is a very effective CSS effect for text, has been isolated in its own class (TW3TextShadow) and is now exposed as TW3CustomControl’s TextShadow property. The class is created on-demand and will not consume memory until used.
  • TW3TagStyle, a class that parses and allows you to add, check and remove CSS style-rules to your control has been updated. The previous parsing routine has been replaced by a much faster string.split() call – removing the need for the for/next loop used to extract styles previously. This class is exposed as TW3CustomControl’s “TagStyle” property. Like Text-Shadow it is created on demand.
    Note: This replaces the older “CssClasses” property which has now been deprecated.
  • The TW3AnimationFrame class has been moved from SmartCL.Components.pas to SmartCL.Animation.pas which seems more fitting.
  • TControlHandleHelper is a strict helper-class for TControlHandle, which is the handle type used for HTML elements in the DOM. Many of the rouge utility functions that were scattered around the RTL have now been isolated here. It now gives you the following functionality:
    • function Defined: boolean;
    • function UnDefined: boolean;
    • function Valid: boolean;
    • function Equals(const Source: TControlHandle): boolean;
    • function Ready: Boolean;
    • function GetChildById(TagId: string): TControlHandle;
    • function GetChildCount: integer;
    • function GetChildByIndex(const Index: integer): TControlHandle;
    • function GetChildOf(TagType: string; var Items: TControlHandleArray): boolean;
    • function GetChildren: TControlHandleArray;
    • function HandleParent: TControlHandle;
    • function HandleRoot: TControlHandle;
    • procedure ReadyExecute(OnReady: TProcedureRef);
    • procedure ReadyExecuteEx(const Tag: TObject; OnReady: TProcedureRefEx);
    • procedure ReadyExecuteAnimFrame(OnReady: TProcedureRef);
  • function w3_GetCursorCSSName, w3_GetCursorFromCSSName has been removed from SmartCL.System.pas – use the methods in the static class TW3MouseCursor instead
  • w3_DOMReady, w3_CSSPrefix and w3_CSSPrefixDef has been removed from SmartCL.System.pas – use the methods in BrowserAPI instead
  • The RTL now has its own stylesheet classes. The unit SmartCL.CSS.StyleSheet.pas and SmartCL.CSS.Classes represents the CSS management units for the system.

    TSuperstyle (see below) generates animation styles on-the-fly and returns the name for it. You can add this name to a control with Control.TagStyle.add().

    TCSS is a helper class for building complex styles via code. TSuperstyle uses this to create the animation effects. Note that these two classes are meant as examples (!) SmartCL.CSS.StyleSheet contains the following classes:

    • TW3StyleSheet
      • property    Handle: TControlHandle
      • property    StyleSheet: JCSSStyleSheet
      • property    StyleRules: JCSSRuleList
      • property    Count: integer
      • property    Items[const Index: integer]: string
      • class function  CreateStyleId: string;
      • class function  CreateStylesheetId: string;
      • class function  CreateStyleElement: TControlHandle;
      • class procedure DisposeStyleElement(const Handle: TControlHandle);
      • class function DefaultStylesheet: TW3StyleSheet;
      • function GetSheetObject: THandle;
      • function GetRuleObject: THandle;
      • function GetCount: integer;
      • function GetItem(const Index: integer): string;
    • TSuperStyle
      • class function EdgeRound(Size: integer): string; overload;
      • class function EdgeRound(TopLeftP, TopRightP, BottomLeftP, BottomRightP: integer):String; overload;
      • class function EdgeTopaz: string;
      • class function EdgeAngaro: string;
      • class function AnimGlow(GlowFrom, GlowTo: TColor): string;
      • class procedure AnimStart(Handle: TControlHandle; animName: String);
    • TCSS
      • function KeyFrames: TCSS;
      • function From: TCSS;
      • function &To: TCSS;
      • function Enter: TCSS;
      • function Leave: TCSS;
      • function PercentOf(Value: integer): TCSS;
      • function IntOf(Value: integer): TCSS;
      • function ColorOf(Value: TColor): TCSS;
      • function &inc(Value: string): TCSS;
      • function CRLF: TCSS;
      • function OpenParam: TCSS;
      • function CloseParam: TCSS;
      • function Background: TCSS;
      • function BoxShadow(Left, Top, Right, Bottom: integer): TCSS;overload;
      • function BoxShadow(Left, Top, Right, Bottom: integer; Color:TColor):TCSS;overload;
      • function LinearGradientV(aFrom, aTo: TColor): TCSS;
      • function LinearGradientH(aFrom, aTo: TColor): TCSS;
      • function LinearGradientTL(aFrom, aTo: TColor): TCSS;
      • function LinearGradientTR(aFrom, aTo: TColor): TCSS;
      • function BeginComplexGradient(Angle: integer): TCSS;
      • function EndComplexGradient: TCSS;
      • function ColorPercent(PercentOf: integer; Color: TColor): TCSS;
      • function LinearGradientAngle(aFrom, aTo: TColor; const Angle: float): TCSS;overload;
      • function LinearGradientAngle(Colors: Array of TColor; const Angle: float): TCSS; overload;
      • function AnimationName(Name: string): TCSS;
      • function AnimationDuration(Secs, MSecs: integer): TCSS;
      • function AnimationInfinite: TCSS;
      • class function Make: TCSS;
      • function Assign(Value: string): TCSS;

Controllers

A new concept has been added to the RTL, namely something called controllers. These are classes that attach to a control and either alter its behavior or exposes functionality that is not available by default.

SmartCL.Controller.EdgeSense

This unit exposes the class TW3EdgeSenseController, which can be attached to any visual control. Once attached the controller will fire events whenever the mouse-pointer or touch is close to the border of the control. This is typically used by controls that is expected to be resized, like a grid header column.

SmartCL.Controller.Swipe

This unit provides the following classes:

  • TW3SwipeControllerOptions
  • TW3SwipeRange
  • TW3SwipeController

The TW3SwipeController attaches itself to any visual control and will trigger an event if a swipe gesture is performed on the target control. Since gesures such as swipe is one of the most common gestures, it made sense to isolate this in particular.

This controller is presently used by one of the new components, TW3Win10Header, which is a html5 implementation of the Windows 10 mobile header. This is a good-looking control that allows you to swipe through categories, much like a page-control for Windows applications, but better suited for mobile designs.

SmartCL.Controller.TagAttributes

This controller implements access to custom data attributes for any visual control. It is essentially the functionality of TW3ElementAttributes (unit: SmartCL.Attributes= but here implemented as a controller.

The class used by the RTL for this behavior today is TW3ElementAttributes, but this will be deprecated in the future in favour of the controller.

SmartCL.Scroll.IScroll

iScroll is now the default scrolling mechanism for visual Smart Mobile Studio applications. The new TW3ScrollWindow and TW3CustomScrollList visual classes all derive their fantastic accurate scrolling and movement capabilities from iScroll – which by many is regarded as the defacto JavaScript scrolling library.

To make iScroll easier to use when writing custom controls that don’t inherit from TW3ScrollWindow or TW3CustomScrollList – you can use the controller instead. Simply create an instance, attach it to the parent container (which contains the elements you want to scroll) and you pretty much have instant iOS / Android scrolling.

Note: $Libraries\IScroll\iScroll.js has been updated to iScroll 5.2.0, which works fine on older Android devices (there were some problems with iScroll before). I have also updated our class implementation to include ALL options, events and functionality.

New Visual Controls

Indeed. While more or more controls is going to be added once the IDE update is in place, we at least have a few new controls in place that can liven up your applications.

First of all, iScroll is now (as explained) the default scroll mechanism in Smart Mobile Studio. When you combine that with the already impressive list of effects (smartcl.effects.pas) writing controls that gives visual feedback when you touch them, that scroll smoothly with momentum, bounce and snap to – is now almost ridiculously simple.

TW3DBGrid

First up is the long-awaited DB grid. This is a fast paced grid that can show thousands of elements without any significant speed penalty. This is because it creates the elements as they come into view – and it uses a very effective cache system to keep track of which elements (rows / cols) have been created, and which needs some attention.

grid

It also allows you to resize and move columns as you would expect from a native grid, so hopefully this first incarnation is just what you expected.

TW3CategoryHeader

Next we have the fancy Windows 10 mobile category header control. This is a touch driven, swipe responsive header that shows both a category and a small ingress text.

categories

This is an excellent control to create with Application.Display as a parent. That way it remains on top regardless of form – and the user can quickly navigate through swipes.

TW3SimpleLabel

We have also added a more lightweight label control. This is best suited for static text that don’t need horizontal alignment. It is a lot more resource friendly than TW3Label which is a composite custom control.

Ace editor control

Ace, the #1 Javascript code editor, akin to Delphi’s SynEdit or the popular Sublime editor, has likewise been added. This should make a lot of things easier, especially if you are creating front-end code for your cloud services (which you can now write in pristine node.js).

ace

TW3LayoutControl

TW3LayoutControl is another nice addition. This is essentially a scrollbox with classical rulers top and left. Perfect for image display programs, paint programs, layout designers and similar tasks. You can override and adapt this heavily and make it the basis for a calendar grid if you like.

We have also completely updated, re-written and quality checked every single control in the RTL. They are now more robust, they update and synchronize with their ready-state more or less like native controls do – and I have spent quite some time making sure they behave as they should.

Oh and yes, there are more controls (this post is getting long).

NodeJS high-level classes

Without a doubt the feature that has received most attention in this update are the Node.JS high level classes and units.

The previous NodeJS namespace and project type gave you more or less the bare-bones of node.js. This could be quite confusing for developers coming straight from Delphi or Lazarus. Especially if you don’t really want to dive to deep into the world of JavaScript – but enjoy some abstraction and ease of use.

The new SmartNJ namespace, where the files also are prefixed with the sane, implements common classes for both clients and servers. Writing an HTTP server is for instance very simple – and the code is in many cases as close to the other server types as possible.

The idea is that if you know how to write one type of server, you should also be able to write other types as well; because they share the same ancestor and commonalities.

As of writing the following server types are supported:

  • UDP
  • HTTP
  • Websocket (WebSocketIO)

The class architecture looks more or less like this:

  • TNJCustomServer
    • TNJUDPService
      • TNJUDPServer
    • TNJHTTPServer
    • TNJWebSocketServer
  • TNJCustomClient
    • TNJUDPService
      • TNJUDPClient
    • TNJHttpClient
    • TNJWebSocketClient

Clients and servers have traditional auxillary objects, such as request and response objects:

  • TNJServerChildClass
    • TNJCustomServerRequest
      • TNJHttpRequest
    • TNJCustomServerResponse
      • TNJHttpResponse

The following units make up the SmartNJ namespace today:

  • SmartNJ.Filesystem
  • SmartNJ.Network.Bindings
  • SmartNJ.Network
  • SmartNJ.Server
  • SmartNJ.Server.Http
  • SmartNJ.Server.Udp
  • SmartNJ.Server.WebSocket
  • SmartNJ.Streams
  • SmartNJ.System
  • SmartNJ.Udp
  • SmartNJ.WebSocket

Unified filesystem

In the previous versions of the RTL we presented several ways to store information. We had thin wrappers over cookies, local storage and filestorage. You could also use one of the 3 database engines to store data (sandboxed).

These unit still ship with the RTL, so should you want to pick one in particular that is of course not a problem. But in order to streamline and make IO as uniform as possible, I have introduced the notion of a standard “filesystem”.

The filesystem base-class can be found (as expected) in the system.filesystem.pas file. This contains the abstract class that makes up the most common filesystem operations.

NodeJS gives you a filesystem class that has direct access to the real files and folders, so be careful when you use that. As expected this filesystem implementation can be found under SmartNJ.Filesystem.pas.

And last but not least you will find a filesystem unit for visual browser applications, under SmartCL.Filesystem.pas.

The idea here is that you can write the exact same code and it will work regardless of which platform you run on.The same code you write for node.js will then save or load data in the browser as well.

This system will be expanded to include Cordova Phonegap, NodeWebkit (a hybrid post-compiler to make desktop applications) and embedded platforms.

Note: NodeJS is special in that it gives you both an asynchronous filesystem and blocking filesystem. If you want to use the blocking version, simply access it via the blocking interface (see unit file). But we strongly suggest that you avoid this since that will have a huge impact on performance. You must never use the blocking IO interface on servers, expect perhaps during startup – it will completely undermine the node.js runtime scheme and yield terrible performance.

Database support

This has been a pressing issue for quite some time, and while we are close, we still need to add more infrastructure before we can wrap the engines we want. Although classes will be available soon – being able to visually hook up data in common RAD style is what we want.

For that to happen I need the following features in the IDE:

  • Datamodules
  • Designer drop-zone for non-visual components
  • Design support for property mapping

Without these features database support under node.js will be flawed at best. But fret not, it is being worked on and we are presently looking at the following engines:

Client side we will provide the same DB API, but here it will wrap the existing engines:

  • WebSQL [deprecated by W3C]
  • SQLite [default]
  • TW3Dataset
  • IndexDB

The reason this is more complicated than it may look is first of all because IndexDB and MongoDB are object based databases, not SQL based. So the architecture really needs property mapping and the IDE to back it up. Naturally it can be done in pure code (which we have started) but the IDE with the built-in database designer is what will make this more sane to work with.

Action support

Yes, finally it is here. TCustomAction and TAction has been added to the RTL, and appropriate controls now expose an Action property that you can assign to.

Again we need the next update before this becomes more easy to work with. You need to be able to drop a TW3ActionManager on your form or datamodule, and then assign that via the property inspector to the control.

The IDE will automatically generate the “glue” code on compile for you, and it will execute as a part of your {$I ‘form: impl’} section and set things up.

Right now you have to create and assign actions manually — but at least it’s finally in place. Which is really good because that allows me to write default-actions for various tasks just like you have in Delphi, C++ builder and Lazarus.

Final words

This has been a preview, not the full load. There are many other changes to the codebase, some of the changes quite radical. Hopefully you find this interesting and will enjoy using it as much as we have enjoyed creating it.

Things that havent been covered yet are:

  • Pixi.js
  • Plumb.js
  • Stacktrace support
  • Codec framework
  • eSpeak
  • Vector graphics display control
  • Support for Scetchpad 3d models
Loading HD 3d models into your smart applications is now possible (and easy). Stills doesnt do this model justice ..

Loading HD 3d models into your smart applications is now possible (and easy). Stills doesnt do this model justice ..

And yes, I have also added support for asset management. This wont be fully visible before the IDE update, but being able to pre-load pictures, sounds and resources before your application starts is now simplicity itself. As a bonus we also added support for Require() which is more or less the de-facto JavaScript way of loading assets (and more) these days. You even get a sweet callback when its done or when something goes wrong.

Stay tuned! I’m working as fast as I can

/Jon

Smart Pascal: Cool tricks for better code

February 2, 2017 1 comment

What is the most costly operation for a HTML5 application? While there are many to pick from the most time-consuming tasks are without a doubt sizing elements and creating elements.

Both of these operations are time-consuming (in cpu terms) because they have a direct impact on the entire layout. In other words, when you create a visible element (which happens when a Smart visual control is created), the entire DOM is affected. The same naturally happens when you adjust the size of an element, because the browser will go through all it’s nodes and re-build its calculated stylesheet (which is a hidden stylesheet that the browser amalgams together).

Since this is the case it makes sense to try to avoid sizing controls as much as possible. You can’t completely avoid it of course – but the less change to width and height the better.

Use the percentages

The Smart Pascal RTL follows the traditional, Delphi and LCL esquire component model. This means that the position and width or height of a control is measured in pixels. But HTML, as you no doubt are aware of, can also work in percentages. This is a lot faster since the browser completely takes care of sizing.

A neat trick you can use is to alter the size of a child control using percentages – and this way avoid any manual calls to Resize(). It all depends on the situation of course, but if (for example) you have a child control that should always be the total width of its parent – you can in fact directly set the width to 100%.

Since the width and height properties are managed by the RTL you want to avoid altering those. They expect values to be in pixels, so changing these values can result in unexpected side-effects. But you can modify the minimum and maximum size styles without affecting the RTL.

w3_setStyle(FContent.Handle, 'min-width', '100%');
w3_setStyle(FContent.Handle, 'min-height', '100%');

The above code takes a child control (“FContent” in this example) and forces it to cover 100% of the parent’s surface. This will work as long as the actual width property does not exceed 100%. If your container is 200 pixels wide, the above code will work fine unless you manually change width to be 201 or more.

I actually use this quite often, especially when I create any form of lists, listboxes or menu systems. Normally the child items is expected to cover the whole width of the parent (a row in a grid for example), with variable height. In that case I can just adjust the height and leave the width to the browser.

Pre calculate content

Like i mentioned above the most time-consuming tasks are size-changes and creation of elements. Of the two, creation of controls is by far the most time consuming for the document object model.

While the RTL gives you controls that are more or less compatible with Delphi or LCL, they have the downside of being quite heavy. There is a lot of code involved which gives the components great depth – but that depth comes at a cost.

What the browser does really fast however, is to create elements in bulk. Just stop and think about it for a while. If creating elements is so time-consuming – then why does pages appear almost instantly? Well, if you examine the webkit HTML renderer you will discover that parsing and creating elements en-mass is highly optimized. This is sadly not the case for the createElement() function that Smart uses to create components at runtime.

What this means is that its faster to create 1000 child elements as raw HTML and inject the text into the DOM – than it is to create 1000 controls. And not just fast: extremely fast!

But then we face a problem, namely that our RTL does something very useful for us: it keeps track of handles for each element and exposes the functionality as object pascal. If we just dump in a ton of HTML then how are we going to locate, use and manipulate our child elements?

A thin wrapper

This is where I tend to use a thin wrapper. This is a class designed to introduce as little code as possible – and only expose the underlying functionality of the document object model. The DOM is actually quite rich in functions even though they can be intimidating at first.

Here is a thin wrapper I use quite a lot.

unit SmartCL.ThinWrapper;

interface

uses
  System.Types, System.Colors,
  SmartCL.System, SmartCL.Graphics, SmartCL.Components,
  SmartCL.Fonts, SmartCL.Borders;

type

  TElementArray = class external
  protected
    function GetItems(const Index : integer) : TControlHandle; external array;
  public
    property length: integer;
    property items[const Index : integer] : TControlHandle read GetItems; default;
  end;

  TWrappedElement = class
  private
    FHandle:  TControlHandle;
  protected
    function  ValueToInt(const Value: variant): integer;
    function  IntToPixels(const Value: integer): string;
    function  GetColor: TColor;
    procedure SetColor(const NewColor: TColor);
  public

    class function GetElementById(const Parent: TControlHandle;
              const ChildId: string): TControlHandle; overload;

    class function GetElementsByType(const Parent: TControlHandle;
              TagName: string;
              var Items: array of TControlHandle): boolean; overload;

  public
    property  Handle: TControlHandle read FHandle;

    property  Id: string
              read  ( FHandle.id )
              write ( Fhandle.id := Value );

    property  Title: string
              read  ( FHandle.title )
              write ( FHandle.title := Value );

    // Offset
    property  OffsetLeft: integer
              read  (ValueToInt(FHandle.offsetLeft))
              write (FHandle.offsetLeft := IntToPixels(Value));

    property  OffsetTop: integer
              read  (ValueToInt(FHandle.offsetTop))
              write (FHandle.offsetTop := IntToPixels(Value));

    property  OffsetWidth: integer
              read  (ValueToInt(FHandle.offsetWidth))
              write (FHandle.offsetWidth := IntToPixels(Value));

    property  OffsetHeight: integer
              read  (ValueToInt(FHandle.offsetHeight))
              write (FHandle.offsetHeight := IntToPixels(Value));

    // Scroll
    property  ScrollLeft: integer
              read  (ValueToInt(FHandle.scrollLeft))
              write (FHandle.scrollLeft := IntToPixels(Value));

    property  ScrollTop: integer
              read  (ValueToInt(FHandle.scrollTop))
              write (FHandle.scrollTop := IntToPixels(Value));

    property  ScrollWidth: integer
              read  (ValueToInt(FHandle.scrollWidth))
              write (FHandle.scrollWidth := IntToPixels(Value));

    property  ScrollHeight: integer
              read  (ValueToInt(FHandle.scrollHeight))
              write (FHandle.scrollHeight := IntToPixels(Value));

    // Client
    property  clientLeft: integer
              read  (ValueToInt(FHandle.clientLeft))
              write (FHandle.clientLeft := IntToPixels(Value));

    property  clientTop: integer
              read  (ValueToInt(FHandle.clientTop))
              write (FHandle.clientTop := IntToPixels(Value));

    property  clientWidth: integer
              read  (ValueToInt(FHandle.clientWidth))
              write (FHandle.clientWidth := IntToPixels(Value));

    property  clientHeight: integer
              read  (ValueToInt(FHandle.clientHeight))
              write (FHandle.clientHeight := IntToPixels(Value));

    // Node
    property  NodeName: string read (FHandle.nodeName);
    property  NodeType: string read (FHandle.nodeType);
    property  NodeValue: variant
              read  (FHandle.nodeValue)
              write (FHandle.nodeValue := Value);

    property  Children: TElementArray read ( TElementArray(FHandle.children) );

    property  InnerHTML: string
              read  ( Handle.innerHTML )
              write ( Handle.innerHTML := Value);

    property  InnerText: string
              read  ( Handle.innerText )
              write ( Handle.innerText := Value);

    property  Color: TColor read GetColor write SetColor;

    function  Wrap(const Handle: TControlHandle): TWrappedElement;

    function  GetElementById(const Id: string): TControlHandle; overload;

    function  GetElementsByType(const TagName: string;
              var Items: array of TControlHandle): boolean; overload;

    procedure Click;

    constructor Create(TagId: string); overload; virtual;
    constructor Create(Parent: TControlHandle; TagId: string); overload; virtual;
    constructor Create(TagHandle: TControlHandle); overload; virtual;
  end;

implementation

//#############################################################################
// TWrappedElement
//#############################################################################

constructor TWrappedElement.Create(TagId: String);
begin
  inherited Create;
  FHandle := BrowserAPI.Body.GetChildById(TagId);
end;

constructor TWrappedElement.Create(TagHandle: TControlHandle);
begin
  inherited Create;
  FHandle := TagHandle;
end;

constructor TWrappedElement.Create(Parent: TControlHandle; TagId: string);
begin
  inherited Create;
  FHandle := Parent.GetChildById(TagId);
end;

procedure TWrappedElement.Click;
begin
  FHandle.click();
end;

function TWrappedElement.Wrap(const Handle: TControlHandle): TWrappedElement;
begin
  result := TWrappedElement.Create( Handle );
end;

function TWrappedElement.IntToPixels(const Value: integer): string;
begin
  result := Value.ToString() + 'px';
end;

function TWrappedElement.ValueToInt(const Value: variant): integer;
begin
  asm
    if (@Value)
    {
      if (typeof(@Value) === "number") {
        @result = @Value
      } else {
        if (typeof(@Value) === "string")
        {
          @Value = parseInt(@Value);
          if (!isNaN(@Value))
            @result = @Value;
        }
      }
    } else {
      @result = 0;
    }
  end;
end;

function TWrappedElement.GetColor: TColor;
begin
  if (FHandle) then
  result := StrToColor( w3_getStyleAsStr(FHandle, 'backgroundColor') ) else
  result := clNone;
end;

procedure TWrappedElement.SetColor(const NewColor: TColor);
begin
  if (FHandle) then
  begin
    if NewColor <> clNone then
    FHandle.style.backgroundColor := ColorToWebStr(NewColor) else
    FHandle.style.backgroundColor := 'transparent';
  end;
end;

class function TWrappedElement.GetElementsByType(const Parent: TControlHandle;
         TagName: string;
         var Items: array of TControlHandle): boolean;
begin
  if (parent) then
  begin
    asm
      @items = (@Parent).getElementsByTagName(@TagName);
    end;
  end else
  Items.Clear();
  result := Items.Count > 0;
end;

function TWrappedElement.GetElementsByType(const TagName: string;
         var Items: array of TControlHandle): boolean;
begin
  if (FHandle) then
  begin
    asm
      @items = (@FHandle).getElementsByTagName(@TagName);
    end;
  end else
  Items.Clear();
  result := Items.Count > 0;
end;

class function TWrappedElement.GetElementById(const Parent: TControlHandle;
         const ChildId: string): TControlHandle;
begin
  if (Parent) then
  begin
    result := Parent.getElementById(ChildId);
    if not (result) then
      result := Parent.getElementById( ChildId.ToLower().Trim() );
  end else
  result := unassigned;
end;

function TWrappedElement.GetElementById(const Id: string): TControlHandle;
begin
  if Id.Length > 0 then
  begin
    result := FHandle.getElementById(Id);
    if not (result) then
      result := FHandle.getElementById( Id.ToLower().Trim() );
  end else
  result := unassigned;
end;

end.

If you are pondering how on earth is that going to help, here is how it works.

All Smart controls are simply JavaScript code designed to manage a real, underlying HTML element. The default element type TW3CustomControl creates is DIV. Controls like TW3TextBox overrides the function that creates this element and replace that with an input element instead. And other controls do the same.

So just because something is not visible to a fully blown TW3CustomControl, doesn’t mean it’s not there. And by using the wrapper we can easily hook rouge or non classified html elements without creating them.

Let’s for example say you inject a bit of HTML into a panel, like this:

procedure TForm1.MakeHTML;
var
  x: integer;
  LHtml: string;
begin
  // make X number of div items
  for x:=1 to 100 do
  begin
    LHtml += Format('
<div id="obj%d">Item #%d</div>
', [x,x]);
  end;

  // inject into our panel
  W3Panel1.InnerHTML := LHtml;
end;

To access one of these DIV elements (which we now have 100 of), we can use our thin wrapper to make it programatically easier:

procedure TForm1.MakeHTML;
var
  x: integer;
  LHtml: string;
  LItem: TWrappedElement;
begin
  // make X number of div items
  for x:=1 to 100 do
  begin
    LHtml += Format('
<div id="obj%d">Item #%d</div>
', [x,x]);
  end;

  // inject into our panel
  W3Panel1.InnerHTML := LHtml;

  // Create wrapper for item #12
  // We pass the handle to the parent (which is the form)
  // and the name. The class will find the element
  LItem := TWrappedElement.Create(W3Panel1.Handle, 'obj12');
end;

Voila! LItem can now be used just like any other Smart control. But remember that this is a thin wrapper, meaning that you are actually digging into the document object model directly.

I should mention a few words about the browser’s calculated stylesheet. One of the things you are going to notice is that width and height is not always going to be correct. This is not due to our code, but because the browser always regards your values as proposals, not absolutes.

So even if you set a control to say, 400px in width – depending on the situation the browser may find it more suitable to set 389px instead. And if you make the mistake of reading it back from the stylesheet – it will report 400px. But this is because the stylesheet is regarded as a proposal.

What you need to do is to write to the stylesheet, but read from the calculated styles. This is why the Smart RTL calls the w3_getStyleAsInt() function when reading these values.

Just so you know in case you think the wrapper is reporting wrong values. It’s not. The browser just works differently because of CSS. Cascading means that styles will merge together, so a button can have 50 gradients assigned to it – and they will all be amalgamated into one and represented in the calculated stylesheet as a single whole.

Other tricks

I could go on for days but I think these two will be handy for now. I would urge you to examine and learn how to use the GetElementByType() and GetElementById() so you get familiar with navigating the DOM like that. GetElementByType() is really cool – it allows you to extract a list of items based on type.

So to get all the DIV elements you can simply do:

var LDivs := LItem.GetElementByType(W3Panel1.Handle, 'div');

I should also mention that a thin-wrapper is only as good as you make it. The code above was never made to do the same as TW3CustomControl can. There is no cosy class wrapping of fonts, constraints, borders or reading of style properties. To enjoy these high-level RTL functions you will have to stick to the RTL.

But, for cases where you want to pre-generate relatively simple elements, like rows in a listbox or some form of menu items – a thin wrapper can mean the difference between useless and brilliant.

Oh, you might be interested in a “Styles” wrapper. I have only fleshed out a handfull of properties, but it does make low-level access a lot easier. If you finish it PM me. The documentation can be found here: http://www.w3schools.com/jsref/dom_obj_style.asp

  TWrappedStyle = class external
  public
    // stretch|center|flex-start|flex-end|space-between|space-around|initial|inherit
    property  alignContent: string;

    // stretch|center|flex-start|flex-end|baseline|initial|inherit
    property  alignItems: string;

    // auto|stretch|center|flex-start|flex-end|baseline|initial|inherit
    property  alignSelf: string;

    // http://www.w3schools.com/jsref/prop_style_animation.asp
    property  animation: string;

    // time|initial|inherit
    property  animationDelay: string;

    // normal|reverse|alternate|alternate-reverse|initial|inherit
    property  animationDirection: string;

    // time|initial|inherit
    property  animationDuration: string;

    // none|forwards|backwards|both|initial|inherit
    property  animationFillMode: string;

    // number|infinite|initial|inherit
    property  animationIterationCount: string;

    // none|keyframename|initial|inherit
    property  animationName: string;

    // linear|ease|ease-in|ease-out|cubic-bezier(n, n, n, n)|initial|inherit
    property  animationTimingFunction: string;

    // running|paused|initial|inherit
    property  animationPlayState: string;

    // color image repeat attachment position size origin clip|initial|inherit
    property background: string;

    // scroll|fixed|local|initial|inherit
    property backgroundAttachment: string;

    // color|transparent|initial|inherit
    property backgroundColor: string;

    // url('URL')|none|initial|inherit
    property backgroundImage: string;

    // http://www.w3schools.com/jsref/prop_style_backgroundposition.asp
    property backgroundPosition: string;

    // repeat|repeat-x|repeat-y|no-repeat|initial|inherit
    property backgroundRepeat: string;

    // border-box|padding-box|content-box|initial|inherit
    property backgroundClip: string;

    // padding-box|border-box|content-box|initial|inherit
    property backgroundOrigin: string;
  end;

Well, more and more tricks will surface, so stay tuned guys!

Smart Pascal: BTree storage anyone?

February 1, 2017 Leave a comment

btreeDictionaries are cool but they are only as good as the mechanisms supporting them. So I figured I could see if we could get more bang for the buck with a dedicated BTree class for Smart Pascal.

If you are wondering what on earth a BTree routine is, head over to Wikipedia and gander up on the technical side here: https://en.wikipedia.org/wiki/B-tree. In short it allows you to store some data connected to a string. Actually, its connected to a value identifier – but we can do a checksum of a string and use that as the key. So dictionary. Sort of.

What is special about the Smart Pascal version? Well, for one it doesn’t use pointers. And secondly you can save it to a stream. Typically classes like this don’t ship with a SaveToStream() method because it’s mostly intended to be used at runtime. But JavaScript have a few perks that force us to think differently.

Hope you enjoy it!


unit BTree;

interface

uses
  System.Types,
  System.Types.Convert,
  System.JSON,
  System.NameValuePairs,
  System.Streams,
  System.Stream.Writer,
  System.Stream.Reader;

type

  TBTreeNode = class(JObject)
  public
    Ident: integer;
    Data: variant;
    Left: TBTreeNode;
    Right: TBTreeNode;
  end;

  TBTreeFileItem = record
    fiIdent:  integer;
    fiData: variant;
  end;

  TBTreeProcess = procedure (const Node: TBTreeNode; var Cancel: boolean);

  TBTree = class(TObject)
  private
    FRoot:  TBTreeNode;
    FCurrent: TBTreeNode;
  public
    property  Root: TBTreeNode read FRoot;
    property  Empty: boolean read ( FRoot = nil );

    function  Add(const Ident: integer; const Data: variant): TBTreeNode;overload;virtual;
    function  Add(const Ident: string; const Data: variant): TBTreeNode;overload;virtual;

    function  &Contains(const Ident: integer): boolean;overload;virtual;
    function  &Contains(const Ident: string): boolean;overload;virtual;

    function  Remove(const Ident: integer): boolean;overload;virtual;
    function  Remove(const Ident: string): boolean;overload;virtual;

    function  Read(const Ident: integer): variant;overload;virtual;
    function  Read(const Ident: string): variant;overload;virtual;

    procedure Write(const Ident: string; const NewData: variant);overload;virtual;
    procedure Write(const Ident: integer; const NewData: variant);overload;virtual;

    procedure Clear;overload;virtual;
    procedure Clear(const Process: TBTreeProcess);overload;virtual;

    function  ToArray: TVarArray;
    function  ToString: string;
    function  Size: integer;

    function  ToJSON: string;
    procedure FromJSON(const Data: string);

    procedure SaveToStream(const Stream: TStream);
    procedure LoadFromStream(const Stream: TStream);

    procedure ForEach(const Process: TBTreeProcess);

    constructor Create;
  end;


implementation


(* These are the IO signatures used for storage *)
const
CNT_BTREE_STREAM_HEADER = $BABE0001;
CNT_BTREE_ITEM_HEADER = $0001BABE;

//#############################################################################
// TBTree
//#############################################################################

constructor TBTree.Create;
begin
  inherited Create;
  FRoot := nil;
  FCurrent := nil;
end;

procedure TBTree.Clear;
begin
  FCurrent := nil;
  FRoot := nil;
end;

procedure TBTree.Clear(const Process: TBTreeProcess);
begin
  ForEach(Process);
  Clear;
end;

function TBTree.ToJSON: string;
begin
  if FRoot<>nil then
    result := Json.stringify(FRoot);
end;

procedure TBTree.FromJSON(const Data: string);
begin
  if not empty then
    Clear;
  FRoot := TBTreeNode( JSON.Parse(data) );
end;

procedure TBTree.SaveToStream(const Stream: TStream);
var
  LWriter: TStreamWriter;
  LData: Array of TBTreeFileItem;
  LRaw: TByteArray;
  LItem: TBTreeFileItem;
begin

  (* First, cache up all the data in an array. We need to do this
     in order to write the node-count on top of the file *)
  ForEach( procedure (const Node: TBTreeNode; var Cancel: boolean)
  begin
    LItem.fiIdent := Node.Ident;
    LItem.fiData := Node.Data;
    LData.add(LItem);
  end);

  LWriter := TStreamWriter.Create(Stream);
  try
    (* Write the magic identifier for the file *)
    LWriter.WriteInteger(CNT_BTREE_STREAM_HEADER);

    (* Write the number of items in the file *)
    LWriter.writeinteger(LData.Count);

    (* Now write each item *)
    for LItem in LData do
    begin
      (* Convert variant to byte-array *)
      LRaw := TDataType.VariantToBytes(LItem.fiData);

      (* Write the identifier for the record *)
      LWriter.WriteInteger(CNT_BTREE_ITEM_HEADER);

      (* Write the item-id *)
      LWriter.WriteInteger(LItem.fiIdent);

      (* Write the # of bytes in the data section *)
      LWriter.WriteInteger(LRaw.Count);

      (* Write the data section *)
      LWriter.Write(LRaw);
    end;
  finally
    LWriter.free;
  end;
end;

procedure TBTree.LoadFromStream(const Stream: TStream);
var
  LReader: TStreamReader;
  LHead: integer;
  LId: integer;
  LBytes: integer;
  LRaw: TByteArray;
  LValue: variant;
  LCount: integer;
begin
  (* Flush content if not empty *)
  if not Empty then
    Clear;

  (* Setup the reader *)
  LReader := TStreamReader.Create(Stream);
  try

    (* Validate the header *)
    LHead := LReader.ReadInteger;
    if LHead = CNT_BTREE_STREAM_HEADER then
    begin
      (* Get the count *)
      LCount := LReader.ReadInteger;

      while LCount>0 do
      begin
        LHead := LReader.ReadInteger;
        if LHead = CNT_BTREE_ITEM_HEADER then
        begin
          (* Read the identifier *)
          LId := LReader.ReadInteger;

          (* read the # of bytes for the variant *)
          LBytes := LReader.ReadInteger;

          (* Flush any lingering data *)
          LRaw.Clear;

          (* Read the raw data that makes up the variant *)
          if LBytes>0 then
          begin
            LRaw := LReader.read(LBytes);

            (* Convert from bytes to intrinsic *)
            LValue := TDatatype.BytesToVariant(LRaw);

            (* Add to tree *)
            self.Add(Lid, LValue);
          end;

        end else
        raise EW3Exception.CreateFmt('Invalid item header, expected %d not %d',
        [CNT_BTREE_ITEM_HEADER,LHead]);

        dec(LCount);
      end;

    end else
    raise EW3Exception.CreateFmt('Invalid stream header, expected %d not %d',
    [CNT_BTREE_STREAM_HEADER,LHead]);

  finally
    LReader.free;
  end;
end;

function TBTree.Size: integer;
var
  LCount: integer;
begin
  ForEach( procedure (const Node: TBTreeNode; var Cancel: boolean)
    begin
      inc(LCount);
    end);
  result := LCount;
end;

function TBTree.ToArray: TVarArray;
var
  Data: TVarArray;
begin
  ForEach( procedure (const Node: TBTreeNode; var Cancel: boolean)
    begin
      Data.add(Node.Ident);
    end);
  result := data;
end;

function TBTree.ToString: string;
begin
  for var x in ToArray do
  begin
    result += TVariant.AsString(x) + #13;
  end;
end;

function TBTree.Add(const Ident: string; const Data: variant): TBTreeNode;
begin
  result := Add( TString.CalcCRC(Ident), Data);
end;

function TBTree.Add(const Ident: integer; const Data: variant): TBTreeNode;
var
  LNode:  TBTreeNode;
begin
  LNode := new TBTreeNode;
  LNode.Ident := Ident;
  LNode.Data := data;

  if (FRoot = nil) then
  FRoot := LNode;

  FCurrent := FRoot;

  while (true) do
  begin
    if (Ident < FCurrent.Ident) then
    begin
      if (FCurrent.left = nil) then
      begin
        FCurrent.left := LNode;
        break;
      end else
      FCurrent := FCurrent.left;
    end else
    if (Ident > FCurrent.Ident) then
    begin
      if (FCurrent.right = nil) then
      begin
        FCurrent.right := LNode;
        break;
      end else
      FCurrent := FCurrent.right;
    end else
    break;
  end;
  result := LNode;
end;

function TBTree.Read(const Ident: string): variant;
begin
  result := Read( TString.CalcCRC(Ident) );
end;

function TBTree.Read(const Ident: integer): variant;
begin
  Result := unassigned;
  FCurrent := FRoot;
  while (FCurrent <> nil) do
  begin
    if (Ident < FCurrent.Ident) then
    FCurrent := Fcurrent.left else
    if (Ident > Fcurrent.Ident) then
    FCurrent := FCurrent.Right else
    begin
      result := FCUrrent.Data;
      break;
    end
  end;
end;

procedure TBTree.Write(const Ident: string; const NewData: variant);
begin
  Write(TString.CalcCRC(Ident), NewData);
end;

procedure TBTree.Write(const Ident: integer; const NewData: variant);
begin
  FCurrent := FRoot;
  while (FCurrent <> nil) do
  begin
    if (Ident < FCurrent.Ident) then
    FCurrent := Fcurrent.left else
    if (Ident > Fcurrent.Ident) then
    FCurrent := FCurrent.Right else
    begin
      FCurrent.Data := NewData;
      break;
    end
  end;
end;

function  TBTree.&Contains(const Ident: string): boolean;
begin
  result := &Contains(TString.CalcCRC(Ident));
end;

function TBTree.&Contains(const Ident: integer): boolean;
begin
  Result := false;
  if FRoot <> nil then
  begin
    FCurrent := FRoot;

    while ( (not Result) and (FCurrent <> nil) ) do
    begin
      if (Ident < FCurrent.Ident) then
      FCurrent := Fcurrent.left else

      if (Ident > Fcurrent.Ident) then
        FCurrent := FCurrent.Right else
      begin
        Result := true;
      end
    end;
  end;
end;

function TBTree.Remove(const Ident: string): boolean;
begin
  result := Remove(TString.CalcCRC(Ident));
end;

function TBTree.Remove(const Ident: integer): boolean;
var
  LFound: boolean;
  LParent: TBTreeNode;
  LChildCount: integer;
  LReplacement,
  LReplacementParent: TBTreeNode;
begin
  LFound := false;
  LParent := nil;
  FCurrent := FRoot;

  while (not LFound) and (FCurrent<>nil) do
  begin
    if (Ident < FCurrent.Ident) then
    begin
      LParent := FCurrent;
      FCurrent:= FCurrent.left;
    end else
    if (Ident > FCurrent.Ident) then
    begin
      LParent := FCurrent;
      FCurrent := FCurrent.right;
    end else
    begin
      LFound := true;
    end;

    if (LFound) then
    begin
      LChildCount:=0;
      if (FCurrent.left<>nil) then inc(LChildCount);
      if (FCurrent.right<>nil) then inc(LChildCount);
      //LChildCount := (if FCurrent.left <> nil then 1 else 0) + (if FCurrent.right <> nil then 1 else 0);
      if (FCurrent = FRoot) then
      begin
        case (LChildCOunt) of
        0: FRoot := nil;
        1: FRoot := if FCurrent.right = nil then FCurrent.left else FCurrent.Right;
        2: begin

            LReplacement := FRoot.left;
            while (LReplacement.right <> nil) do
            begin
              LReplacementParent := LReplacement;
              LReplacement := LReplacement.right;
            end;

            if (LReplacementParent <> nil) then
            begin
              LReplacementParent.right := LReplacement.Left;
              LReplacement.right := FRoot.Right;
              LReplacement.left := FRoot.left;
            end else
            LReplacement.right := FRoot.right;
          end;
        end;

        FRoot := LReplacement;
      end else
      begin
        case LChildCount of
        0:  if (FCurrent.Ident < LParent.Ident) then
            Lparent.left  := nil else
            LParent.right := nil;
        1:  if (FCurrent.Ident < LParent.Ident) then
            begin
              if (FCurrent.Left = NIL) then
              LParent.left := FCurrent.Right else
              LParent.Left := FCurrent.Left;
            end else
            begin
              if (FCurrent.Left = NIL) then
              LParent.right := FCurrent.Right else
              LParent.right := FCurrent.Left;
            end;
        2:  begin
              LReplacement := FCurrent.left;
              LReplacementParent := FCurrent;

              while (LReplacement.right <> nil) do
              begin
                LReplacementParent := LReplacement;
                LReplacement := LReplacement.right;
              end;
              LReplacementParent.right := LReplacement.left;

              LReplacement.right := FCurrent.right;
              LReplacement.left := FCurrent.left;

              if (FCurrent.Ident < LParent.Ident) then
              LParent.left := LReplacement else
              LParent.right := LReplacement;
            end;
          end;
        end;
      end;
  end;

  result := LFound;
end;

procedure TBTree.ForEach(const Process: TBTreeProcess);

  function ProcessNode(const Node: TBTreeNode): boolean;
  begin
    (* Default to false. If true is defined here, the operation
       has been canceled by the user *)
    result := false;

    if (Node <> nil) then
    begin

      (* Process left path first *)
      if (node.left <> nil) then
      begin
        result := ProcessNode(Node.left);
        if result then
        exit;
      end;

      (* process midpoint *)
      Process(Node, result);
      if result then
      exit;

      (* current right path *)
      if (Node.right <> nil) then
      begin
        result:=ProcessNode(Node.right);
        if result then
        exit;
      end;
    end;
  end;

begin
  ProcessNode(FRoot);
end;

end.

Smart Pascal: Support for next generation Amiga A1222

January 30, 2017 2 comments

If you are between 35 and 45 years old, chances are you remember the Commodore Amiga. This was the major computer to have back in the 80’s and 90’s. And if you know your history you also know that it was way ahead of all the other systems out there. Microsoft prides itself on inventing multi-tasking, but truth is that Amiga OS had a full multitasking, window oriented desktop way back in 1984.

amiga_os41_b_lancastria

Classic AmigaOS, skinnable and very flexible even today

Before you think this post is about the old computers, let me just say that it’s not; I realize that most of you will immediately think retro when you hear the word Amiga – but fact is that Amiga is doing somewhat of a comeback these days.

A brand new operating system

AmigaOS was awesome for its time and really was a system from the future. In an age where the average PC was a monochrome green dos experience, and Mac’s were ridicules black and white boxes – the Amiga was like something out of a spaceship. PC’s didn’t even have a mouse, let alone any form of multimedia – and here was a powerhouse of a machine that had a full windowing desktop, bright and crisp colors, co-processors dedicated to graphics, sound and dispatching — people had never seen anything like it.

Sadly, due to some borderline insane management decisions, Commodore went bankrupt back in 1994. You would think that the Amiga just went away after that, but it’s still going to this day. Two decades after Commodore went out of business – people still write software for the platform and some of the coolest hardware you will ever see is released for it every year. It really is an astounding computer and community. I mean, only recently in 2016 at least 5 new games were released for the platform (!)

Needless to say, the old operating system that culminated in OS 3.9 is hopelessly trapped in the past. Which is why the people who bought the rights to the system have spent more than a few years bringing it up to speed (right now it’s at version 4.1). And the result speaks for itself. It really is one of the best looking desktop systems in the world.

Sexy, fast and modern: Amiga OS 4.1

Sexy, fast and modern: Amiga OS 4.1

Brand new hardware

Say what you want about Amiga hardware but the quality was outstanding. But outstanding hardware from the early 90’s can’t hope to compete with modern computers. We are used to GPU chips that spin millions of pixels around the screen per second without breaking a sweat; 24 bit sound, monstrous 3d graphics processors, multi threading, gigabytes of ram and terabytes of storage space (phew!).

With Commodore long dead and no company officially in charge of hardware, a man called Trevor Dickinson decided that he had enough. Back in the day everyone was waiting for Commodore to release the Amiga 5000 (the Amiga 4000 was the Commodore flagship back in the 90s, sadly that was the end of it), including Trevor. This dream of the next Amiga is something everyone that loved the platform share even to this day. Well, Trevor decided to do something about the situation. He picked up the gauntlet, sharpened his sword and his journey towards a new Amiga began.

The Amiga X1000 is an earlier PPC based model

The Amiga X1000 is an earlier PPC based model

Without going into too much nitty-gritty here, Trevor and his colleagues set out to create a PPC based motherboard capable of running Amiga OS 4. And yes, the operating system is presently PPC based. This is due to the fact that in the 90’s both the Amiga and Apple Mac’s used the MC68k line of processors. These were retired and replaced with PPC chips. This made perfect sense at the time. Since Apple used them in their computers – the Amiga could enjoy cheaper hardware and piggyback on Apples success.

Most people today think that PPC is an obsolete chipset, but fact is that they are still in production – largely so, because they are popular for embedded systems. The obscene problems with heating is no longer as pressing at it used to be due to changes in production and materials – and as a consequence the new Amiga uses PPC even now.

I have two monster PPC based Mac’s in my basement so I was a bit timid when I heard that OS 4.x was using PPC. I mean the top of the line powermac is more cooling metal than content. But thankfully that line of PPC chips is a thing of the past.

Two exciting models

There have been a ton of Commodore related attempts to get the Amiga back on the market, every five years or so someone buys the Commodore name and tries to revive the glory days of the Amiga (or so it seems). All of them have failed largely due to internal fighting between the many owners that has rights to various bits and pieces. If there is a reason that the Amiga never managed to get back into market – this is it. The rights to various parts of the system was auctioned off to the highest bidder, with the operating system going one way, chips going another and name and intellectual property. It has been a complete cluster fuck since that horrible day back in 1994.

Trevor on the other hand has solved this the only way a reasonable and sane human being can. Which is to simply abandon and leave the old hardware, the Commodore name and the old machines peculiarities where they belong; In the past. So what if you can’t slap Commodore on the physical machine? The new Amiga was not designed to be a 30 year old computer. It was designed from scratch to run Amiga OS 4 and to be a system based on the same principles as the original; And that is (at least to me) what really counts. I enjoyed the old games and demo scene, but the desktop and programming aspects of the Amiga was always where my heart was.

Trevor is an avid collector and have pretty much every Commodore model you can think of

Trevor is an avid collector and have pretty much every Commodore model you can think of

Besides, kids that grew up in the aftermath of Commodore going under – don’t have a clue what the Amiga was anyhow. Nor do they have any relationship to Commodore as a brand. And considering 20 years have passed -clinging to these old names is ultimately a complete waste of time and money. So Trevor’s being fed up and deciding to build a whole new machine for the updated and modernized Amiga operating system – that makes perfect sense to me.

Unlike previous attempts by various individuals and groups, Trevor’s work is more tangible. In fact, you can go out right now and order the high-end PPC based monster that is the Amiga x5000. This is the most expensive model and it’s going to set you back a whopping €1800. This may seem like a steep price but bear in mind the amount of work and cost of parts – so it’s actually not that bad.

img_5293

The ultra sexy X5000 is a high-end Amiga

As a modern developer my computer needs have grown exponentially every year. I no longer install my development tools on my actual PC, instead I use VMWare and have all my development platforms neatly organized as virtual machines. The result is that I can no longer buy the cheap or middle-range offerings. My latest PC cost me around €2500. So all things considered €1800 is in the same range as an Amiga 4000 went for back in the day. This is a work horse that professionals use, it’s not a low-end gaming model.

The A1222

The A1222 “Tabour” motherboard is a cheaper, more affordable entry machine. This is the one I’m getting to start with

Thankfully there is a cheaper model in the pipeline. I have already pre-ordered the A1222 which retails at around 400€ (give or take vat and shipping). This has a smaller CPU, less ram (you can of course stuff more in yourself) and you have to get your own mini-atx cabinet for it. As the name hints to this is equivalent to a bog standard A1200, which was my all time favorite. My old gem of a machine had a 40 megabyte harddisk, 4 megabytes of ram and an external CS-ROM. These specs are ridicules today, even my phone has more .. well, everything really; but 20 years ago that setup was the bomb.

When people talk retro like I do now, always remember that the Amiga operating system was able to deliver a close to Windows 95 experience. That should tell you something about how fast, well written and efficient the old Amiga was.

This was a machine that delivered an impressive and modern desktop experience even by today’s standards in 512 Kb (yes you read that right, kilobytes) of working ram. Imagine what an OS based on the same principles can do with 4 gigabytes of ram, 32 bit graphics, protected and paged memory, 24 bit audio and all the perks of modern computing.

Where emulation can’t go

Those that follow my blog know that I Amiga emulation and retro computing. So whenever I have time I whip out my Raspberry PI 3b based Amiga and enjoy my Amiga games, desktop and even do a spot of programming. We even managed to get Freepascal 3.x to compile on it, which means I can write applications with all the cool features of modern object-oriented programming at my disposal. On an Amiga 4000 emulated – the mind boggles when you think about this to long.

Compiling anything, even older stuff that is a joke by today standard, is painful on the Raspberry PI. Here showing my retro-fitted A500 PI with sexy led keyboard. It will soon get a makeover with an UP board :)

Here showing my retro-fitted A500 PI with sexy led keyboard, overclocked to the hilt running at 4 times the speed of an original Amiga 4000 (for $35!)

Sadly you can forget running Amiga OS 4 under emulation on the PI. Sure, it works fine on Windows or Linux – but emulation is only as good as the code emulating it. And while the PPC emulation layer is a monumental achievement and I applaud the original author, it’s not nearly as optimized as the older 68k layer. From what I know it’s still running ad-hoc with no JIT involved (or at least not cache’d). This means that you need a powerful PC to really enjoy Amiga OS 4. Emulation is a bit like bootleg movies, and a poor bootleg will ruin the movie completely. You can get away with a mid-range x86 PC I suppose, but you can forget about ARM or x86 embedded boards.

Perhaps you remember that I tested various embedded boards a while back? Part of my motivation in doing that (and buying HW for €300 on a whim) was to find a reasonably priced x86 or ARM single board computer that could emulate OS 4 without problems. The most expensive and capable of the boards I tested was the UP x86 board that retails at around $150 (I got the biggest model they had on offer). And yes, it did manage to run Amiga OS 4, just not in a way that made it usable or enjoyable. On my kick ass Intel i7 based PC it emulates just fine, but again – it becomes ridicules not buying a real Amiga since emulation will cost me more or the same. I mean, why not go for the real deal if its affordable?

So if a €400 Amiga is what it takes to run OS 4 properly, then I have no problem supporting Trevor’s work and get the real deal.

Freepascal and Smart

While Trevor doesn’t know me personally, I am a huge fan of his endeavour. And one of the things I want to start porting to the shiny new OS 4.x platform is of course – Smart Pascal. If you have ever heard of Turbo Pascal and Delphi (with emphasis on the latter) then you get some idea of what Smart is. Except that we compile for node.js and the browser rather than machine-code.

So before I can port that over I will have to get Chromium run on OS 4, and then link that up with Freepascal. I do enjoy C++ but its not even close to the productivity of object pascal so I prefer to work in that. And since freepascal 3.x has thankfully been ported already that is not a problem. So with a bit of work I think Delphi developers will be in for a treat, and people new to programming will love learning object pascal.

Porting the full might of Smart Mobile Studio to the Amiga is going to take an effort, but I think it will be worth it

Porting the full might of Smart Mobile Studio to the Amiga is going to take an effort, but I think it will be worth it

But naturally, not everyone is used to building from the ground up. It will take some work and probably weeks of adaptation before the full might of Smart Mobile Studio runs on the Amiga. But when it does – it will have a huge impact. Because then you can use the Amiga to create cloud servers, mobile applications for ALL platforms and much, much more!

So if being a part of using an operating system from the grass-root and up sounds exciting, why not take a peek at the A1222 or Amiga x5000?

Head over to http://www.a-eon.com/ and have a gander