Tag Archives: Programming

On Friday 9th October 2015, eHUG (the European HyperCard User Group) organised a LiveCode meeting in Breda, the Netherlands. At the meeting, we exchanged ideas and experiences with LiveCode. Subjects that came to table were RunRev’s support service, Beat Cornaz’ composing software (obviously created with LiveCode), HyperStudio and HyperDuino, and the Arduino.

group1

Because we talked so much about Arduino, we didn’t have much time to do experiments, but we did have a live demonstration of the Arduino. We raffled off a copy of the book Programming LiveCode for the Real Beginner and a license of HyperStudio.

group2

While Claudi was not able to attend the meeting, he offered to send us his Arduino stack. Everyone who was present at the meeting will receive his e-mail address (as well as those of all other participants).
If we find the time to organise it, we’ll have the next meeting in spring. If you’re interested in attending the meeting, contact the organiser through this web form.

group3

See also here for more a little more information about the HyperDuino and the location of this edition of the meeting.

The book that was raffled off is also available here.
You can read everything about HyperDuino on this website.

Beta Version of Installer Maker 1.8.7 Available!

Recently, We got a request to make some improvements to Installer Maker. As a result, we have made many, many invisible changes, fixing bugs and adding new features. In a few places, the changes are visible. For example, you can see the long list with all special folder paths for Windows (XP up to and including 8.1) and you can see a small but essential change on the Scripts pane, allowing for pre- and post-uninstall script execution.

It is now possible to select any special Windows folder as a destination folder.

It is now possible to select any special Windows folder as a destination folder.

Installer Maker 1.8.7b03 can be downloaded here.

Below follows an overview of all changes in the latest beta version.

  • Installers created with a trial copy on Windows should now start correctly (only on the same computer as on which they were created)
  • The documentation (on the Help screen) should still show up, even if the httpHeaders were set incorrectly by another script
  • install locations have been extended by a large number of Windows-specific directories
  • the destination “startup disk” appeared incorrectly
  • a possibility to create an installer with empty files has been added
  • the uninstaller is now installed in the executable’s folder on Windows, while previously it would end up in a separate folder occasionally
  • file paths are now included in profile files
  • if a path in a profile file is not found, you’ll be asked if you want to repair all paths
  • on Windows, the Uninstaller can now execute LiveCode scripts that have been added on the Scripts pane; thise feature will be added for Mac OS X soon
Installer Maker Scripts Pane

Installer Maker Scripts Pane

Note that this is still a beta version. If you want to create an installer for distribution, download and use Installer Maker from the official website. Tests show that Installer Maker 1.8.7b03 isn’t fully compatible with LiveCode 7 yet, due to bugs in LiveCode 7. Installer Maker 1.8.7b03 is only available as a plug-in for the commercial license of LiveCode. A standalone version will be made available shortly.

We need your help! Please, download and test Installer Maker 1.8.7b03 and send an e-mail with your findings to Economy-x-Talk’s support department! Everybody can download the beta. Installers created with an unregister trial copy only work on the computer they were created on. This version of Installer Maker is a free update for everyone with a current license.

Make a Button Respond to Device Movements

I am making a simple game for Android devices. In this game, the user moves a control by turning the device to the left or to the right.¬† I noticed that the orientationChanged message wasn’t sent.¬† After some experimentation, I found out that all mobileOrientation features were broken, but the mobileAcceleration features worked. However, accelerationChanged message was never sent. I don’t know if these problems are LiveCode bugs or malfunctions of my own device, although I do have a feeling that something is wrong with LiveCode’s mobileOrientation and mobileAcceleration features.

Anyway, the point is that I found a way to make my game work correctly and I decided to write down how I did it. This is just a quick note. If you find anything to improve, just post it in a comment at the bottom of this text.

Screen capture of the game

Before you can use the acceleration features of your device, you have to set up this feature for your app. I do this in the openCard handler to make the time between starting the app and rendering the first card as short as possible. It doesn’t matter whether your stack starts up in landscape or portrait mode. The acceleration values will adjust automatically.

Because you might want to play your game on desktop machines too, you need to check if the game is played on a movile device, using the environment function. Not all devices have acceleration features, so you have to check for those too, using the mobileSensorAvailable function. The parameter of this function can contain “acceleration”, “rotation”, “heading”, or “rotation rate”. For my game, I need “acceleration”. This is sufficient to make a control move accordingly the movements of the device.

on openCard
  if the environment is "mobile" then
    put mobileSensorAvailable("acceleration") into lSensorAvailable
    if lSensorAvailable then
      mobileStartTrackingSensor "acceleration",false
    else
      answer warning "Acceleration sensors are unavailable."
    end if
  else
    put false into lSensorAvailable
  end if
  // first finish rendering, then send setupCard message
  send "setupCard" to me in 0 millisecs
  pass openCard
end openCard

If the sensor is available, we can turn it on using the mobileStartTrackingSensor command. The first parameter of this command determines the sensor. The second parameter toggles loosely, but power-saving sensor readings. If this parameter is false, it will read accurate values but also use a lot of energy. I have chosen to use accurate values for my game.

I want the openCard handler to finish before I start reading sensor values. The reason is that the card will render only after the openCard handler finishes and I want this to happen as quickly as possible. By using the send in time command, I make sure that the setupCard command is executed after the openCard handler finishes and the card is rendered.

on setupCard
  if the environment is "mobile" then
    pollForRotation
  end if
end setupCard

The setupCard message can also be used to put controls in the centre of the card, to set up a score display, or do download some data from a server for instance. We don’t need that functionality in this simple example.

The pollForRotation handler actually doesn’t poll for rotation. It polls for acceleration, but my original intention was to get rotation values and hence the name. Actually, this is what makes me think that something is wrong with LiveCode. Perhaps RunRev got a few wires switched under the hood.

The pollForRotation handler first reads the device’s position. The more you turn your device to the left or right, the lower (less than 0) or higher (greater than 0) the value returned by the mobileSensorReading function.

Although polling might seem inefficient and power intensive (as polling usually is), it has two big advantages. First, polling the mobileSensorReading function works, whereas the accelerationChanged message doesn’t seem to work. Second, the documentation about the mobileEnableAccelerometer¬† command suggests that the accelerationChanged message can’t be sent more often than 1 time per second –when this message becomes functional in LiveCode, I’ll have to test if the mobileEnableAccelerometer command accepts fractions of seconds.

The pollForRotation handler reads the acceleration value, stores it, and executes the moveControl handler. After the moveControl handler finishes, the pollForRotation handler executes itself again in 150 milliseconds. By keeping the reading frequency low (almost 7 times per second), I hope to save some energy and leave enough time for other game controls to be updates and rendered.

on pollForRotation
   put mobileSensorReading("acceleration") into myDelta
   moveControl item 2 of myDelta // horizontal, left-right movement
   send "pollForRotation" to me in 150 milliseconds 
end pollForRotation

The mobileSensorReading function returns a list of three values: front-back horizontal movement, left-right horizontal movement and vertical movement. We need the second item from this list.

Another screen capture of the game

The moveControl handler does some calculations and moves the button. The moveControl handler needs a paremeter, indicating the amount of left-right movement. We don’t want the game to be unresponsive but at the same time we don’t want it to be too sensitive. Therefore, we make two adjustments. First, we calculate the square value of the horizontal movement. If the player moves the device quickly to the left or to the right, the game will respond accordingly. However, if the plater moves the device slowly, the game will make small adjustments, which allows for high accuracy. We’re also increasing overall speed, by multiplying the square value by 1.2. We’re doing this just because we thought the game was a bit slow at first. You can change this number as you like. The variable myHorizontalSpeed now contains the number of pixels by which we want to move the control. We use the sign of theDirection to determine whether we want to go left or right.

on moveControl theDirection
  // for demo, assume 1 button exists
   put 1.2*theDirection^2 into myHorizontalSpeed // the movement of the button at a time in pixels
   if theDirection < -1 then
      set the left of btn 1 to max(0,the left of btn 1 - myHorizontalSpeed)
   else if theDirection > 1 then
      set the right of btn 1 to min(the right of this cd,the right of btn 1 + myHorizontalSpeed)   end if
end moveControl

If theDirection is very small, i.e. between -1 and 1, we don’t move the control. This may happen when the player accidentally moves the device or if the device appears to be extremely sensitive. If theDirection is less than 1, we move the button to the left and if theDirection is greater than 1, we move the button to the right.

The left side of a button can be at any location above 0. The minimum value for the left of a button is 0. Therefore, whenever the new value of the left of the button is going to be less than 0, we set the left to 0. In all other cases, we set the left of the button to a value slightly less than its current value, if theDirection < -1.

The right of a button can be at any location below the right of the card. The maximum value of the right of the button equals the right of the card. Therefore, whenever the new values of the right of the button is going to be larger than the right of the card, we set the right of the button to the right of the card. In all other cases, we set the right of the button to a value slightly higher than its current value, if theDirection > 1.

If the button isn’t at the extreme left or extreme right location, the amount by which we move the button is determined by the variable myHorizontalSpeed.

If you want to stop the game, you only need to stop the pollForRotation handler. E.g. you can write the following in a button script:

Zn mouseUp
  put the pendingMessages into myMessages
  filter myMessages with "*pollForRotation*"
  repeat for each line myMsg in myMessages
    cancel item 1 of myMsg
  end repeat
end mouseUp

To start playing, just call the pollForRotation handler again.

New: LiveCode GUI Elements for iOS

This is a collection of GUI elements for iOS that we are going to use in a LiveCode project.

These GUI elements are really cool because

  • objects scale appropriately if you drag their handles
  • scroll views are created automatically for scrolling fields
  • lists can be easily adjusted using a property
  • all buttons hilite nicely
  • all elements can easily be mixed with LiveCode’s objects and your own or third-party custom objects
  • this collection of GUI elements costs nothing if you have already made your contribution

Ios_gui

The stack is completely editable and you can copy all items to your own project.

This collection of GUI elements is available to anyone who has registered as a contributor here. Once you have registered, you can obtain the GUI elements for iOS from our website.

LiveCode or RealStudio Drawer Windows – Which Bug Do You Pick?

Here are two sample projects, which I just made to test LiveCode and RealBasic drawer windows.

LiveCode works fine, until you change the drawer window size by script.

Screen_shot_2011-09-23_at_01

RealStudio works fine until you start moving the window around.

0screen_shot_2011-09-23_at_01

I think I’d choose LiveCode in this case, but perhaps it is better to avoid using drawer windows completely.

Zip files and folders (including empty folders) with LiveCode

/*
iZip 1.0.1
Make a zip file recursively without the
need for a callback message. This script includes empty folders
if there are any.
—————————————————————-
Usage: iZip path[,true|false]

Parameters:
The first parameter is a string containing the path to a file or
folder. The zip file will have the same name as the original
file or folder, appended by “.zip”, and is created in the same
directory as the original.
The second parameter is a boolean excluding “.DS_Store” files if
true and includes them if false or empty.

Error messages:
If the zip external encounters a problem, an  error message is
returned of the form “error: file not open”.

Comment:
You need to write your own script to check that the revZip
external is available.

Copyright © 2011 by Economy-x-Talk
http://www.economy-x-talk
This script is free to use. Please give credit if you do. Always
include this copyright notice in a visible place in your
software.
*/
on iZip theFile,theExcludeDsStore
     if not (there is a file theFile or there is a folder theFile) then
          return “error: file not found”
     end if
     set the itemDel to slash
     put number of items of theFile into myAbsolutPathLength
     put theFile into myFolderlist
     put theFile & “.zip” into myZipFile
     // need to zip the whole thing recursively
     // item names should probably be the relative folder paths
     put the directory into myOldDir
     put 0 into myCounter
     revZipOpenArchive myZipFile,”write”
     put the result into rslt
     if rslt is not empty then
          set the itemDel to comma
          return “error:” && item 2 of rslt
     end if
     if there is a file theFile then
          put last item of theFile into myItemName
          revZipAddItemWithFile myZipFile,myItemName,theFile
          put the result into rslt
          if rslt is not empty then
               set the itemDel to comma
               return “error:” && item 2 of rslt
          end if
     else // folder
          repeat forever with messages
               add 1 to myCounter
               put line 1 of myFolderList into myCurrentFolder
               // add folder
               put item myAbsolutPathLength to -1 of
                      (myCurrentFolder & “//”) into myItemName
               revZipAddItemWithFile myZipFile,myItemName,””
               put the result into rslt
               if rslt is not empty then
                    set the itemDel to comma
                    return “error:” && item 2 of rslt
               end if
               set the directory to myCurrentFolder
               put the files into myFileList
               // zip the files
               repeat for each line myFile in myFileList with messages
                    if (myFile is “.” or myFile is “..”) or
                           theExcludeDsStore is true and myFile is
                           “.DS_Store” then next repeat
                    put item myAbsolutPathLength to -1 of
                           (myCurrentFolder & slash & myFile) into
                           myItemName
                    if char -4 to -1 of myFile is “.zip” then
                         revZipAddUncompressedItemWithFile
                                myZipFile,myItemName,myFile
                    else
                         revZipAddItemWithFile myZipFile,myItemName,myFile
                    end if
                    wait 0 millisecs with messages
               end repeat
               put the folders into myTempFolderList
               repeat for each line myFolder in myTempFolderList
                    if myFolder is “.” or myFolder is “..” then next repeat
                    put cr & myCurrentFolder & slash & myFolder after
                           myFolderList
               end repeat
               delete line 1 of myFolderList
               if number of lines of myFolderList is 0 then exit repeat
               wait 0 millisecs with messages
          end repeat
     end if
     revZipCloseArchive myZipFile
     put the result into rslt
     if rslt is not empty then
          set the itemDel to comma
          return “error:” && item 2 of rslt
     end if
     set the directory to myOldDir
end iZip

A One-Liner to Keep Track of User Preferences in LiveCode

I have kept this a secret for a long time, but after using this in many projects, it is now time to publish my user preferences library for LiveCode/Revolution.

Below follow the scripts without comments. I will write more about it in a future blog post.

function saveGlobalsToVar theGlobals
  do “global” && theGLobals
  put theGlobals & return into tempVar
  repeat for each item x in theGlobals
    do “put urlEncode(” & x & “) & return after tempVar”
  end repeat
  return tempVar
end saveGlobalsToVar

on readVarToGlobals theVar
     — breakpoint
     put line 1 of theVar into myGlobals
     delete line 1 of theVar
     do “global” && myGlobals
     repeat with x = 1 to number of items of myGlobals
          put “put urlDecode(line x of theVar) into” && item x of myGlobals into myScript
          do myScript
     end repeat
end readVarToGlobals

on writePrefs theGlobals,thePrefsFilename
  put saveGlobalsToVar(theGlobals) into myPrefs
  put prefsFile(thePrefsFilename) into myPrefsFile
  set the filetype to “sysppref”
  open file myPrefsFile for binary write
  write compress(myPrefs) to file myPrefsFile
  close file myPrefsFile
end writePrefs

on readPrefs thePrefsFilename
  put prefsFile(thePrefsFilename) into thePrefsFilename
  if there is a file thePrefsFilename then
    open file thePrefsFilename for binary read
    read from file thePrefsFilename until EOF
    close file thePrefsFilename
    put decompress(it) into myPrefs
    — put myPrefs
    readVarToGlobals myPrefs
  else
    return “Error: File does not exist.”
  end if
end readPrefs

function prefsFile theFilename
     if gU3App is true then
          put $u3_app_data_path & slash & theFilename into myFilePath
          replace backslash with slash in myFilePath
     else if the platform is “MacOS” then
          put specialFolderpath(“preferences”) & “/” & theFilename into myFilePath
     else if the platform is “win32” then
          put specialFolderpath(26) & “/” & theFilename & “.ini” into myFilePath
     else if the platform is “Linux” then
          put $HOME & slash & dot & theFilename into myFilePath
     else
          put stackToFilename(theFilename) into myFilePath
     end if
     return myFilePath
end prefsFile

Speakable Password Generator

Here’s a script, which I just posted to the Revolution forum. I post it on my blog, because I like the script and it might be useful to many people. It should also be simple to translate this to PHP for instance.

This script generates passwords with length theLength, choosing consonents and vowels semi-randomly such as to keep the password speakable. This function allows for double consonants while still keeping the password speakable.

 

function speakablePassword theLength
     put “bcdfghjklmnprstwyz” into myConsonants
     put “aeiou” into myVowels
     put “” into myPass
     repeat myPasswordLength
          if char -1 of myPass is in myConsonants then
               if char -2 of myPass is in myConsonants or
                  length(myPass = 9) then
                    put any char of myVowels after myPass
               else
                    if any item of “true,false” then
                         put any char of myVowels after myPass
                    else
                         put any char of myConsonants after myPass
                    end if
               end if
          else
               put any char of myConsonants after myPass
          end if
     end repeat
     return myPass
end speakablePassword

CSV to Tab conversion with RunRev

The csv to tab delimited conversion script, which I just posted on the Revolution Use List. I post it here just in case it is useful to someone. This script doesn’t take returns and tabs inside cells in consideration.

function csv2tab theData
    repeat for each line myLine in theData
         repeat for each word myWord in myLine
              if char 1 of myWord is quote and (char -2 of myWord is
               quote or char -1 of myWord is quote) then
                   replace comma with “<#>” in myWord
              end if
              put myWord after myNewData
         end repeat
         put cr after myNewData
    end repeat
    replace comma with tab in myNewData
    replace “<#>” with comma in myNewData
    return myNewData
end csv2tab