https://wiki.freepascal.org/api.php?action=feedcontributions&user=Dbannon&feedformat=atomFree Pascal wiki - User contributions [en]2024-03-29T13:09:22ZUser contributionsMediaWiki 1.35.6https://wiki.freepascal.org/index.php?title=LHelp&diff=159036LHelp2024-03-22T22:33:38Z<p>Dbannon: /* See also */</p>
<hr />
<div>== Overview ==<br />
LHelp is the default help viewer for Lazarus. It displays CHM help files.<br />
<br />
The screenshot below shows Lazarus LCL context-sensitive help displayed in LHelp:<br />
[[File:lhelp.png]]<br />
<br />
== Operation ==<br />
LHelp can be run stand-alone and used to open/view CHM files like a regular CHM viewer.<br />
<br />
It can also be invoked and controlled by the Lazarus IDE to display context-sensitive help etc.<br />
For that, it uses the [[Help protocol]].<br />
<br />
== Internals ==<br />
LHelp uses the [[chm]] package provided by FPC to read CHM files, table of contents (TOC), and full text search index.<br />
<br />
== Limitations ==<br />
* LHelp cannot search on words containing the search term. This is similar to the behaviour of the Windows help viewer. Implementing this would require traversing the entire full-text search tree, parsing results and then displaying which would probably take a lot of time.<br />
* Known bugs: See bug reports on the bug tracker with tag LHelp, LHelp in the description etc.<br />
<br />
== See also ==<br />
* [[Add Help to Your Application]]<br />
* [[chmhelp]]<br />
* [[htmlhelp compiler]]<br />
<br />
[[Installing Help in the IDE]] - How to install help for the RTL, FCL and LCL in the Lazarus IDE<br />
[[Category:Lazarus]]<br />
[[Category:Help and Docs]]<br />
[[Category:CHM]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=1st_things_to_do_post_install&diff=1590351st things to do post install2024-03-22T22:32:35Z<p>Dbannon: Added a link to the page people actually want.</p>
<hr />
<div><br />
Once your Lazarus is installed and able to start up, it is good practice to:<br />
<br />
* Install .chm integrated {{keypress|F1}} help system [[Lazarus_Documentation#Lazarus.2FFPC_.28Free_Pascal.29|manually]] (i.e. copy the [http://www.stack.nl/~marcov/doc-chm.zip .chm files] into the .../chm subdirectory of your local installation, '''F1''' will then find the files). Please see [[Installing Help in the IDE]] - How to install help for the RTL, FCL and LCL in the Lazarus IDE.<br />
* Familiarise yourself with, and use, the button '''Project Options / Test''' .<br />
* Have a brief look at the [[IDE tricks]].<br />
<br />
[[Category:Lazarus]]<br />
[[Category:IDE]]<br />
[[Category:Install]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Building_a_Pico_Compiler&diff=158398Building a Pico Compiler2024-03-10T10:05:19Z<p>Dbannon: type</p>
<hr />
<div>=Building a Pico Compiler=<br />
<br />
One of FPC's great features is its ability to provide cross compilers. A cross compiler allows you to run on one system and make a binary for another, possibly completely different system. When making binaries for embedded targets, its the only way to go !<br />
<br />
=== fpcupdeluxe ===<br />
For most users, the most convenient way to get a cross compiler for the Raspberry Pi Pico is to use [https://github.com/LongDirtyAnimAlf/fpcupdeluxe fpcupdeluxe] to do it all. It will install just FPC or FPC and Lazarus for you. Note it installs the 'main' version of each, from time to time, one or the other may have bugs (thats the nature of development branches) but not often. Try again in a day or so if you have problems. <br />
<br />
But this page is really for those who want to build their own, or just understand the process. Maybe adapt it to another CPU. These instructions focus on using a 64bit Linux system as the host, Windows and perhaps Mac users can probably do something similar.<br />
<br />
Much more details at [[ARM Embedded Tutorial - Installing Lazarus and Free Pascal]]<br />
<br />
{{Note| and this is important, this recipe produces a different file layout than fpupdeluxe. That is for simplicity. Do not mix this with a fpcupdeluxe or results may be unpredictable.}}<br />
<br />
===The constraints===<br />
* If you are a Lazarus user, it makes sense to have a stand alone Lazarus install for your Pico work, you could switch a single install (by changing Tools->Options->Files->Compiler_executable) but it could get annoying. Multiple installs means installing Lazarus from source, remarkably easy.<br />
* If you already have a Source install of FPC main December 2023 or later, you can use that, with some care. Again, might be easier and safer to have a dedicated version. You do need an existing install of FPC => 3.2.2, if yours is installed in read only space (installed from a deb or rpm) then I suggest you work here with with a fresh copy of FPC source and "install" it into your own space.<br />
<br />
===Dependencies===<br />
* An existing, working, FPC322 or later.<br />
* Install binutils-arm-none-eabi - that means ones for arm, no operating system (or embedded) and eabi indicates the arm subtype, no 'hf' means 'soft float'. <br />
* And, of course, you need Michael's git repo with pico-fpcexamples. These come from https://github.com/michael-ring/pico-fpcexamples Note that Michael's repo contain two separate things, some nicely set example projects and, in 'units' a number of libraries (*.obj) and pascal units to link them into a pascal project.<br />
<br />
===The Process===<br />
Here are a series of command lines, I do not recommend you run them as a script (although I have) because you MUST check for errors at each step. I keep this in my home directory in a structure like this, change it of course but change the commands if you do ! The commands here will create two fpc-3.3.1 directories, if you have them already, ensure they are empty or change the commands. Note that I have already downloaded fpc.zip to my Downloads dir.<br />
<br />
===Assumed Dir Structure===<br />
$HOME/bin/FPC<br />
fpc-3.2.2 .....<br />
fpc-3.3.1<br />
SRC<br />
fpc-3.2.2 ....<br />
fpc-3.3.1<br />
<br />
=====The commands=====<br />
<br />
#!/bin/bash<br />
export FPC_VER="fpc-3.3.1" # note 1<br />
export SRC_DIR="$HOME/testFPC/FPC/SRC/$FPC_VER"<br />
export FPC_DIR="$HOME/testFPC/FPC/$FPC_VER"<br />
UNITDIR="$FPC_DIR"/units/arm-embedded/armv6m<br />
<br />
mkdir -p "$FPC_DIR"<br />
mkdir -p "$SRC_DIR"<br />
cd "$SRC_DIR"<br />
unzip ~/Downloads/fpc.zip # note 2<br />
cd fpc<br />
make all # note 3<br />
make install INSTALL_PREFIX="$FPC_DIR"<br />
<br />
# note 4<br />
make crossinstall CPU_TARGET=arm OS_TARGET=embedded SUBARCH=armv6m CROSSBINDIR=/usr/bin BINUTILSPREFIX=arm-none-eabi- CROSSOPT="-CpARMV6M -CaEABI" INSTALL_PREFIX="$FPC_DIR" INSTALL_UNITDIR="$UNITDIR"<br />
<br />
cp compiler/ppcx64 "$FPC_DIR"/bin/ppcx64 # note 5<br />
cp compiler/ppcrossarm "$FPC_DIR"/bin/ppcrossarm # note 6<br />
<br />
cp rtl/embedded/arm/* "$UNITDIR" # note 7<br />
<br />
cd "$FPC_DIR"<br />
mkdir -p etc<br />
bin/fpcmkcfg -d basepath="$FPC_DIR" -o etc/fpc.cfg # note 8<br />
<br />
cd bin<br />
echo "#!/bin/sh" > fpc.bash # note 9<br />
echo "# A script that ensure when we start fpc here, it ignores all other fpc.cfg" >> fpc.bash<br />
echo "$FPC_DIR""/bin/fpc -n @$FPC_DIR/etc/fpc.cfg \"\$@\"" >> fpc.bash<br />
chmod u+x fpc.bash<br />
<br />
<br />
* note 1 - right now (March 2024) fpc main thinks of it self as 3.3.1, this may change after the next release of FPC and its possible the necessary updates to make this all work will not be in that release.<br />
<br />
* note 2 - make sure you have that fpc.zip file there, FPC main Dec 2024 or later. cd ~/Downloads; wget https://gitlab.com/freepascal.org/fpc/source/-/archive/main/source-main.zip<br />
<br />
* note 3 - this takes a while.<br />
<br />
* note 4 - this takes a while too. And note it places the RTL files in a different place than fpcupdeluxe.<br />
<br />
* note 5 - this is your host system compiler.<br />
<br />
* note 6 - and this is the cross compiler, we call fpc and it calls this one.<br />
<br />
* note 7 - these are some units that the RTL, built earlier.<br />
<br />
* note 8 - this creates a pretty stock standard fpc.cfg file. <br />
<br />
* note 9 - this makes a script that calls fpc and passes parameters that ensure fpc takes no regard of any other fpc.cfg file, only the one we just made. The fpc.cfg that controls here is the one in note 8.<br />
<br />
{{Note | Whats different from fpcupdeluxe ? This model puts the Run Time Libraries in $FPC_DIR/units/arm-embedded/armv6m whereas fpcupdeluxe puts them in a more correct $FPC_DIR/units/arm-embedded/armv6m/eabi/rtl. The problem with the fpcupdeluxe model is that fpc does not look there by default, a patch to fpc.cfg is needed. As this is intended as a simple demo, I decided to keep it as simple as possible. If you prefer, change the UNITDIR variable to "$FPC_DIR"/units/arm-embedded/armv6m/eabi/rtl and patch your fpc.cfg to include in its search path. }}<br />
<br />
===Setup a Lazarus project===<br />
<br />
Doing your Pico development in Lazarus does make things simpler once setup. You can use (some of) Lazarus's help and code tools systems, click compile (but obviously not run) and have all your errors nicely presented. You do not need to make any changes to Lazarus (apart from settings mentioned below), just about any recent Lazarus will do. But, as mentioned above, makes sense to dedicate an install to Pico because of an unusual setting, next papa. <br />
<br />
Start a new Lazarus project in the usual way, to keep life simple, save it in a directory under 'projects' in Michael's directory tree. <br />
Firstly, a one off for this Lazarus instance, note this is NOT saved with the project. Open Options -> Files and look for the "Compiler executable" entry. Put in, with a full path, 'fpc.bash'. eg /home/YOUR-USER-NAME/bin/FPC/fpc-3.3.1/bin/fpc.bash (note fpcupdeluxe uses fpc.sh)<br />
<br />
Then, open the Project Options and set the following -<br />
<br />
Paths<br />
Other unit files (-Fu): ../pico-fpcexamples/units<br />
Include files (-Fi) : $(ProjOutDir)<br />
Libraries (-Fl);<br />
Unit output directory (-FU): lib/$(TargetCPU)-$(TargetOS)<br />
Target file name (-o) : bin/project1<br />
[x] Apply conventions<br />
<br />
Config and Target<br />
[x] Use standard compiler config...<br />
[ ] Use additional<br />
Target OS (-T) : Embedded<br />
Target CPU family (-P) : arm<br />
Target processor (-Cp) : ARMV6M<br />
<br />
Custom Options<br />
-Wpraspi_pico<br />
-godwarfsets<br />
-godwarfcpp<br />
-Xu<br />
-XParm-none-eabi-<br />
<br />
Note the last of the Custom Options, when you installed binutils-arm-none-eabi, you added a set of binutils for this particular processor. Some systems may call them arm-embedded-??? if yours uses that name scheme, change that Option accordingly.<br />
<br />
===Compile from Commandline===<br />
Its also reasonable to build from the command line, here is an example -<br />
<br />
#!/bin/bash<br />
echo "------- Using the fpc from 3.3.1 ---------"<br />
export MICHAELS_UNITS="$HOME"/Pico2/projects/pico-fpcexamples/units <br />
"$HOME"/bin/FPC/fpc-3.3.1/bin/fpc.bash -va -Xu -XParm-none-eabi- -Tembedded -Parm -CpARMV6M -Fu"$MICHAELS_UNITS" -Wpraspi_pico project1<br />
<br />
Note: -XParm-none-eabi- indicates how the binutils are named. If yours are named differently, change it !<br />
<br />
===See also===<br />
<br />
* [[ARM Embedded Tutorial - FPC and the Raspberry Pi Pico|ARM Embedded Raspberry Pi Pico Tutorials]]<br />
<br />
<br />
[[Category:Operating Systems and Platforms]]<br />
[[Category:ARM]]<br />
[[Category:Microcontrollers]]<br />
[[Category:Embedded]]<br />
[[Category:Tutorials]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Building_a_Pico_Compiler&diff=158397Building a Pico Compiler2024-03-10T10:02:55Z<p>Dbannon: /* Dependencies */</p>
<hr />
<div>=Building a Pico Compiler=<br />
<br />
One of FPC's great features is its ability to provide cross compilers. A cross compiler allows you to run on one system and make a binary for another, possibly completely different system. When making binaries for embedded targets, its the only way to go !<br />
<br />
=== fpcupdeluxe ===<br />
For most users, the most convenient way to get a cross compiler for the Raspberry Pi Pico is to use [https://github.com/LongDirtyAnimAlf/fpcupdeluxe fpcupdeluxe] to do it all. It will install just FPC or FPC and Lazarus for you. Note it installs the 'main' version of each, from time to time, one or the other may have bugs (thats the nature of development branches) but not often. Try again in a day or so if you have problems. <br />
<br />
But this page is really for those who want to build their own, or just understand the process. Maybe adapt it to another CPU. These instructions focus on using a 64bit Linux system as the host, Windows and perhaps Mac users can probably do something similar.<br />
<br />
Much more details at [[ARM Embedded Tutorial - Installing Lazarus and Free Pascal]]<br />
<br />
{{Note| and this is important, this recipe produces a different file layout than fpupdeluxe. That is for simplicity. Do not mix this with a fpcupdeluxe or results may be unpredictable.}}<br />
<br />
===The constraints===<br />
* If you are a Lazarus user, it makes sense to have a stand alone Lazarus install for your Pico work, you could switch a single install (by changing Tools->Options->Files->Compiler_executable) but it could get annoying. Multiple installs means installing Lazarus from source, remarkably easy.<br />
* If you already have a Source install of FPC main December 2023 or later, you can use that, with some care. Again, might be easier and safer to have a dedicated version. You do need an existing install of FPC => 3.2.2, if yours is installed in read only space (installed from a deb or rpm) then I suggest you work here with with a fresh copy of FPC source and "install" it into your own space.<br />
<br />
===Dependencies===<br />
* An existing, working, FPC322 or later.<br />
* Install binutils-arm-none-eabi - that means ones for arm, no operating system (or embedded) and eabi indicates the arm subtype, no 'hf' means 'soft float'. <br />
* And, of course, you need Michael's git repo with pico-fpcexamples. These come from https://github.com/michael-ring/pico-fpcexamples Note that Michael's repo contain two separate things, some nicely set example projects and, in 'units' a number of libraries (*.obj) and pascal units to link them into a pascal project.<br />
<br />
===The Process===<br />
Here are a series of command lines, I do not recommend you run them as a script (although I have) because you MUST check for errors at each step. I keep this in my home directory in a structure like this, change it of course but change the commands if you do ! The commands here will create two fpc-3.3.1 directories, if you have them already, ensure they are empty or change the commands. Note that I have already downloaded fpc.zip to my Downloads dir.<br />
<br />
===Assumed Dir Structure===<br />
$HOME/bin/FPC<br />
fpc-3.2.2 .....<br />
fpc-3.3.1<br />
SRC<br />
fpc-3.2.2 ....<br />
fpc-3.3.1<br />
<br />
=====The commands=====<br />
<br />
#!/bin/bash<br />
export FPC_VER="fpc-3.3.1" # note 1<br />
export SRC_DIR="$HOME/testFPC/FPC/SRC/$FPC_VER"<br />
export FPC_DIR="$HOME/testFPC/FPC/$FPC_VER"<br />
UNITDIR="$FPC_DIR"/units/arm-embedded/armv6m<br />
<br />
mkdir -p "$FPC_DIR"<br />
mkdir -p "$SRC_DIR"<br />
cd "$SRC_DIR"<br />
unzip ~/Downloads/fpc.zip # note 2<br />
cd fpc<br />
make all # note 3<br />
make install INSTALL_PREFIX="$FPC_DIR"<br />
<br />
# note 4<br />
make crossinstall CPU_TARGET=arm OS_TARGET=embedded SUBARCH=armv6m CROSSBINDIR=/usr/bin BINUTILSPREFIX=arm-none-eabi- CROSSOPT="-CpARMV6M -CaEABI" INSTALL_PREFIX="$FPC_DIR" INSTALL_UNITDIR="$UNITDIR"<br />
<br />
cp compiler/ppcx64 "$FPC_DIR"/bin/ppcx64 # note 5<br />
cp compiler/ppcrossarm "$FPC_DIR"/bin/ppcrossarm # note 6<br />
<br />
cp rtl/embedded/arm/* "$UNITDIR" # note 7<br />
<br />
cd "$FPC_DIR"<br />
mkdir -p etc<br />
bin/fpcmkcfg -d basepath="$FPC_DIR" -o etc/fpc.cfg # note 8<br />
<br />
cd bin<br />
echo "#!/bin/sh" > fpc.bash # note 9<br />
echo "# A script that ensure when we start fpc here, it ignores all other fpc.cfg" >> fpc.bash<br />
echo "$FPC_DIR""/bin/fpc -n @$FPC_DIR/etc/fpc.cfg \"\$@\"" >> fpc.bash<br />
chmod u+x fpc.bash<br />
<br />
<br />
* note 1 - right now (March 2024) fpc main thinks of it self as 3.3.1, this may change after the next release of FPC and its possible the necessary updates to make this all work will not be in that release.<br />
<br />
* note 2 - make sure you have that fpc.zip file there, FPC main Dec 2024 or later. cd ~/Downloads; wget https://gitlab.com/freepascal.org/fpc/source/-/archive/main/source-main.zip<br />
<br />
* note 3 - this takes a while.<br />
<br />
* note 4 - this takes a while too. And note it places the RTL files in a different place than fpcupdeluxe.<br />
<br />
* note 5 - this is your host system compiler.<br />
<br />
* note 6 - and this is the cross compiler, we call fpc and it calls this one.<br />
<br />
* note 7 - these are some units that the RTL, built earlier.<br />
<br />
* note 8 - this creates a pretty stock standard fpc.cfg file. <br />
<br />
* note 9 - this makes a script that calls fpc and passes parameters that ensure fpc takes no regard of any other fpc.cfg file, only the one we just made. The fpc.cfg that controls here is the one in note 8.<br />
<br />
{{Note | Whats different from fpcupdeluxe ? This model puts the Run Time Libraries in $FPC_DIR/units/arm-embedded/armv6m whereas fpcupdeluxe puts them in a more correct $FPC_DIR/units/arm-embedded/armv6m/eabi/rtl. The problem with the fpcupdeluxe model is that fpc does not look there by default, a patch to fpc.cfg is needed. As this is intended as a simple demo, I decided to keep it as simple as possible. If you prefer, change the UNITDIR variable to "$FPC_DIR"/units/arm-embedded/armv6m and patch your fpc.cfg to include in its search path. }}<br />
<br />
===Setup a Lazarus project===<br />
<br />
Doing your Pico development in Lazarus does make things simpler once setup. You can use (some of) Lazarus's help and code tools systems, click compile (but obviously not run) and have all your errors nicely presented. You do not need to make any changes to Lazarus (apart from settings mentioned below), just about any recent Lazarus will do. But, as mentioned above, makes sense to dedicate an install to Pico because of an unusual setting, next papa. <br />
<br />
Start a new Lazarus project in the usual way, to keep life simple, save it in a directory under 'projects' in Michael's directory tree. <br />
Firstly, a one off for this Lazarus instance, note this is NOT saved with the project. Open Options -> Files and look for the "Compiler executable" entry. Put in, with a full path, 'fpc.bash'. eg /home/YOUR-USER-NAME/bin/FPC/fpc-3.3.1/bin/fpc.bash (note fpcupdeluxe uses fpc.sh)<br />
<br />
Then, open the Project Options and set the following -<br />
<br />
Paths<br />
Other unit files (-Fu): ../pico-fpcexamples/units<br />
Include files (-Fi) : $(ProjOutDir)<br />
Libraries (-Fl);<br />
Unit output directory (-FU): lib/$(TargetCPU)-$(TargetOS)<br />
Target file name (-o) : bin/project1<br />
[x] Apply conventions<br />
<br />
Config and Target<br />
[x] Use standard compiler config...<br />
[ ] Use additional<br />
Target OS (-T) : Embedded<br />
Target CPU family (-P) : arm<br />
Target processor (-Cp) : ARMV6M<br />
<br />
Custom Options<br />
-Wpraspi_pico<br />
-godwarfsets<br />
-godwarfcpp<br />
-Xu<br />
-XParm-none-eabi-<br />
<br />
Note the last of the Custom Options, when you installed binutils-arm-none-eabi, you added a set of binutils for this particular processor. Some systems may call them arm-embedded-??? if yours uses that name scheme, change that Option accordingly.<br />
<br />
===Compile from Commandline===<br />
Its also reasonable to build from the command line, here is an example -<br />
<br />
#!/bin/bash<br />
echo "------- Using the fpc from 3.3.1 ---------"<br />
export MICHAELS_UNITS="$HOME"/Pico2/projects/pico-fpcexamples/units <br />
"$HOME"/bin/FPC/fpc-3.3.1/bin/fpc.bash -va -Xu -XParm-none-eabi- -Tembedded -Parm -CpARMV6M -Fu"$MICHAELS_UNITS" -Wpraspi_pico project1<br />
<br />
Note: -XParm-none-eabi- indicates how the binutils are named. If yours are named differently, change it !<br />
<br />
===See also===<br />
<br />
* [[ARM Embedded Tutorial - FPC and the Raspberry Pi Pico|ARM Embedded Raspberry Pi Pico Tutorials]]<br />
<br />
<br />
[[Category:Operating Systems and Platforms]]<br />
[[Category:ARM]]<br />
[[Category:Microcontrollers]]<br />
[[Category:Embedded]]<br />
[[Category:Tutorials]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Building_a_Pico_Compiler&diff=158396Building a Pico Compiler2024-03-10T10:01:34Z<p>Dbannon: tweaks</p>
<hr />
<div>=Building a Pico Compiler=<br />
<br />
One of FPC's great features is its ability to provide cross compilers. A cross compiler allows you to run on one system and make a binary for another, possibly completely different system. When making binaries for embedded targets, its the only way to go !<br />
<br />
=== fpcupdeluxe ===<br />
For most users, the most convenient way to get a cross compiler for the Raspberry Pi Pico is to use [https://github.com/LongDirtyAnimAlf/fpcupdeluxe fpcupdeluxe] to do it all. It will install just FPC or FPC and Lazarus for you. Note it installs the 'main' version of each, from time to time, one or the other may have bugs (thats the nature of development branches) but not often. Try again in a day or so if you have problems. <br />
<br />
But this page is really for those who want to build their own, or just understand the process. Maybe adapt it to another CPU. These instructions focus on using a 64bit Linux system as the host, Windows and perhaps Mac users can probably do something similar.<br />
<br />
Much more details at [[ARM Embedded Tutorial - Installing Lazarus and Free Pascal]]<br />
<br />
{{Note| and this is important, this recipe produces a different file layout than fpupdeluxe. That is for simplicity. Do not mix this with a fpcupdeluxe or results may be unpredictable.}}<br />
<br />
===The constraints===<br />
* If you are a Lazarus user, it makes sense to have a stand alone Lazarus install for your Pico work, you could switch a single install (by changing Tools->Options->Files->Compiler_executable) but it could get annoying. Multiple installs means installing Lazarus from source, remarkably easy.<br />
* If you already have a Source install of FPC main December 2023 or later, you can use that, with some care. Again, might be easier and safer to have a dedicated version. You do need an existing install of FPC => 3.2.2, if yours is installed in read only space (installed from a deb or rpm) then I suggest you work here with with a fresh copy of FPC source and "install" it into your own space.<br />
<br />
===Dependencies===<br />
* An existing, working, FPC322. <br />
* Install binutils-arm-none-eabi - that means ones for arm, no operating system (or embedded) and eabi indicates the arm subtype, no 'hf' means 'soft float'. <br />
* And, of course, you need Michael's git repo with pico-fpcexamples. These come from https://github.com/michael-ring/pico-fpcexamples Note that Michael's repo contain tow separate things, some nicely set example projects and, in 'units' a number of libraries (*.obj) and pascal units to link them into a pascal project.<br />
<br />
===The Process===<br />
Here are a series of command lines, I do not recommend you run them as a script (although I have) because you MUST check for errors at each step. I keep this in my home directory in a structure like this, change it of course but change the commands if you do ! The commands here will create two fpc-3.3.1 directories, if you have them already, ensure they are empty or change the commands. Note that I have already downloaded fpc.zip to my Downloads dir.<br />
<br />
===Assumed Dir Structure===<br />
$HOME/bin/FPC<br />
fpc-3.2.2 .....<br />
fpc-3.3.1<br />
SRC<br />
fpc-3.2.2 ....<br />
fpc-3.3.1<br />
<br />
=====The commands=====<br />
<br />
#!/bin/bash<br />
export FPC_VER="fpc-3.3.1" # note 1<br />
export SRC_DIR="$HOME/testFPC/FPC/SRC/$FPC_VER"<br />
export FPC_DIR="$HOME/testFPC/FPC/$FPC_VER"<br />
UNITDIR="$FPC_DIR"/units/arm-embedded/armv6m<br />
<br />
mkdir -p "$FPC_DIR"<br />
mkdir -p "$SRC_DIR"<br />
cd "$SRC_DIR"<br />
unzip ~/Downloads/fpc.zip # note 2<br />
cd fpc<br />
make all # note 3<br />
make install INSTALL_PREFIX="$FPC_DIR"<br />
<br />
# note 4<br />
make crossinstall CPU_TARGET=arm OS_TARGET=embedded SUBARCH=armv6m CROSSBINDIR=/usr/bin BINUTILSPREFIX=arm-none-eabi- CROSSOPT="-CpARMV6M -CaEABI" INSTALL_PREFIX="$FPC_DIR" INSTALL_UNITDIR="$UNITDIR"<br />
<br />
cp compiler/ppcx64 "$FPC_DIR"/bin/ppcx64 # note 5<br />
cp compiler/ppcrossarm "$FPC_DIR"/bin/ppcrossarm # note 6<br />
<br />
cp rtl/embedded/arm/* "$UNITDIR" # note 7<br />
<br />
cd "$FPC_DIR"<br />
mkdir -p etc<br />
bin/fpcmkcfg -d basepath="$FPC_DIR" -o etc/fpc.cfg # note 8<br />
<br />
cd bin<br />
echo "#!/bin/sh" > fpc.bash # note 9<br />
echo "# A script that ensure when we start fpc here, it ignores all other fpc.cfg" >> fpc.bash<br />
echo "$FPC_DIR""/bin/fpc -n @$FPC_DIR/etc/fpc.cfg \"\$@\"" >> fpc.bash<br />
chmod u+x fpc.bash<br />
<br />
<br />
* note 1 - right now (March 2024) fpc main thinks of it self as 3.3.1, this may change after the next release of FPC and its possible the necessary updates to make this all work will not be in that release.<br />
<br />
* note 2 - make sure you have that fpc.zip file there, FPC main Dec 2024 or later. cd ~/Downloads; wget https://gitlab.com/freepascal.org/fpc/source/-/archive/main/source-main.zip<br />
<br />
* note 3 - this takes a while.<br />
<br />
* note 4 - this takes a while too. And note it places the RTL files in a different place than fpcupdeluxe.<br />
<br />
* note 5 - this is your host system compiler.<br />
<br />
* note 6 - and this is the cross compiler, we call fpc and it calls this one.<br />
<br />
* note 7 - these are some units that the RTL, built earlier.<br />
<br />
* note 8 - this creates a pretty stock standard fpc.cfg file. <br />
<br />
* note 9 - this makes a script that calls fpc and passes parameters that ensure fpc takes no regard of any other fpc.cfg file, only the one we just made. The fpc.cfg that controls here is the one in note 8.<br />
<br />
{{Note | Whats different from fpcupdeluxe ? This model puts the Run Time Libraries in $FPC_DIR/units/arm-embedded/armv6m whereas fpcupdeluxe puts them in a more correct $FPC_DIR/units/arm-embedded/armv6m/eabi/rtl. The problem with the fpcupdeluxe model is that fpc does not look there by default, a patch to fpc.cfg is needed. As this is intended as a simple demo, I decided to keep it as simple as possible. If you prefer, change the UNITDIR variable to "$FPC_DIR"/units/arm-embedded/armv6m and patch your fpc.cfg to include in its search path. }}<br />
<br />
===Setup a Lazarus project===<br />
<br />
Doing your Pico development in Lazarus does make things simpler once setup. You can use (some of) Lazarus's help and code tools systems, click compile (but obviously not run) and have all your errors nicely presented. You do not need to make any changes to Lazarus (apart from settings mentioned below), just about any recent Lazarus will do. But, as mentioned above, makes sense to dedicate an install to Pico because of an unusual setting, next papa. <br />
<br />
Start a new Lazarus project in the usual way, to keep life simple, save it in a directory under 'projects' in Michael's directory tree. <br />
Firstly, a one off for this Lazarus instance, note this is NOT saved with the project. Open Options -> Files and look for the "Compiler executable" entry. Put in, with a full path, 'fpc.bash'. eg /home/YOUR-USER-NAME/bin/FPC/fpc-3.3.1/bin/fpc.bash (note fpcupdeluxe uses fpc.sh)<br />
<br />
Then, open the Project Options and set the following -<br />
<br />
Paths<br />
Other unit files (-Fu): ../pico-fpcexamples/units<br />
Include files (-Fi) : $(ProjOutDir)<br />
Libraries (-Fl);<br />
Unit output directory (-FU): lib/$(TargetCPU)-$(TargetOS)<br />
Target file name (-o) : bin/project1<br />
[x] Apply conventions<br />
<br />
Config and Target<br />
[x] Use standard compiler config...<br />
[ ] Use additional<br />
Target OS (-T) : Embedded<br />
Target CPU family (-P) : arm<br />
Target processor (-Cp) : ARMV6M<br />
<br />
Custom Options<br />
-Wpraspi_pico<br />
-godwarfsets<br />
-godwarfcpp<br />
-Xu<br />
-XParm-none-eabi-<br />
<br />
Note the last of the Custom Options, when you installed binutils-arm-none-eabi, you added a set of binutils for this particular processor. Some systems may call them arm-embedded-??? if yours uses that name scheme, change that Option accordingly.<br />
<br />
===Compile from Commandline===<br />
Its also reasonable to build from the command line, here is an example -<br />
<br />
#!/bin/bash<br />
echo "------- Using the fpc from 3.3.1 ---------"<br />
export MICHAELS_UNITS="$HOME"/Pico2/projects/pico-fpcexamples/units <br />
"$HOME"/bin/FPC/fpc-3.3.1/bin/fpc.bash -va -Xu -XParm-none-eabi- -Tembedded -Parm -CpARMV6M -Fu"$MICHAELS_UNITS" -Wpraspi_pico project1<br />
<br />
Note: -XParm-none-eabi- indicates how the binutils are named. If yours are named differently, change it !<br />
<br />
===See also===<br />
<br />
* [[ARM Embedded Tutorial - FPC and the Raspberry Pi Pico|ARM Embedded Raspberry Pi Pico Tutorials]]<br />
<br />
<br />
[[Category:Operating Systems and Platforms]]<br />
[[Category:ARM]]<br />
[[Category:Microcontrollers]]<br />
[[Category:Embedded]]<br />
[[Category:Tutorials]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Building_a_Pico_Compiler&diff=158395Building a Pico Compiler2024-03-10T09:59:16Z<p>Dbannon: update fpcupdeluxe note</p>
<hr />
<div>=Building a Pico Compiler=<br />
<br />
One of FPC's great features is its ability to provide cross compilers. A cross compiler allows you to run on one system and make a binary for another, possibly completely different system. When making binaries for embedded targets, its the only way to go !<br />
<br />
=== fpcupdeluxe ===<br />
For most users, the most convenient way to get a cross compiler for the Raspberry Pi Pico is to use [https://github.com/LongDirtyAnimAlf/fpcupdeluxe fpcupdeluxe] to do it all. It will install just FPC or FPC and Lazarus for you. Note it installs the 'main' version of each, from time to time, one or the other may have bugs (thats the nature of development branches) but not often. Try again in a day or so if you have problems. <br />
<br />
But this page is really for those who want to build their own, or just understand the process. Maybe adapt it to another CPU. These instructions focus on using a 64bit Linux system as the host, Windows and perhaps Mac users can probably do something similar.<br />
<br />
Much more details at [[ARM Embedded Tutorial - Installing Lazarus and Free Pascal]]<br />
<br />
{{Note| and this is important, this recipe produces a different file layout than fpupdeluxe. That is for simplicity. Do not mix this with a fpcupdeluxe or results may be unpredictable.}}<br />
<br />
===The constraints===<br />
* If you are a Lazarus user, it makes sense to have a stand alone Lazarus install for your Pico work, you could switch a single install (by changing Tools->Options->Files->Compiler_executable) but it could get annoying. Multiple installs means installing Lazarus from source, remarkably easy.<br />
* If you already have a Source install of FPC main December 2023 or later, you can use that, with some care. Again, might be easier and safer to have a dedicated version. You do need an existing install of FPC322, if yours is installed in read only space (installed from a deb or rpm) then I suggest you work here with with a fresh copy of FPC and "install" it into your own space. <br />
<br />
===Dependencies===<br />
* An existing, working, FPC322. <br />
* Install binutils-arm-none-eabi - that means ones for arm, no operating system (or embedded) and eabi indicates the arm subtype, no 'hf' means 'soft float'. <br />
* And, of course, you need Michael's git repo with pico-fpcexamples. These come from https://github.com/michael-ring/pico-fpcexamples Note that Michael's repo contain tow separate things, some nicely set example projects and, in 'units' a number of libraries (*.obj) and pascal units to link them into a pascal project.<br />
<br />
===The Process===<br />
Here are a series of command lines, I do not recommend you run them as a script (although I have) because you MUST check for errors at each step. I keep this in my home directory in a structure like this, change it of course but change the commands if you do ! The commands here will create two fpc-3.3.1 directories, if you have them already, ensure they are empty or change the commands. Note that I have already downloaded fpc.zip to my Downloads dir.<br />
<br />
===Assumed Dir Structure===<br />
$HOME/bin/FPC<br />
fpc-3.2.2 .....<br />
fpc-3.3.1<br />
SRC<br />
fpc-3.2.2 ....<br />
fpc-3.3.1<br />
<br />
=====The commands=====<br />
<br />
#!/bin/bash<br />
export FPC_VER="fpc-3.3.1" # note 1<br />
export SRC_DIR="$HOME/testFPC/FPC/SRC/$FPC_VER"<br />
export FPC_DIR="$HOME/testFPC/FPC/$FPC_VER"<br />
UNITDIR="$FPC_DIR"/units/arm-embedded/armv6m<br />
<br />
mkdir -p "$FPC_DIR"<br />
mkdir -p "$SRC_DIR"<br />
cd "$SRC_DIR"<br />
unzip ~/Downloads/fpc.zip # note 2<br />
cd fpc<br />
make all # note 3<br />
make install INSTALL_PREFIX="$FPC_DIR"<br />
<br />
# note 4<br />
make crossinstall CPU_TARGET=arm OS_TARGET=embedded SUBARCH=armv6m CROSSBINDIR=/usr/bin BINUTILSPREFIX=arm-none-eabi- CROSSOPT="-CpARMV6M -CaEABI" INSTALL_PREFIX="$FPC_DIR" INSTALL_UNITDIR="$UNITDIR"<br />
<br />
cp compiler/ppcx64 "$FPC_DIR"/bin/ppcx64 # note 5<br />
cp compiler/ppcrossarm "$FPC_DIR"/bin/ppcrossarm # note 6<br />
<br />
cp rtl/embedded/arm/* "$UNITDIR" # note 7<br />
<br />
cd "$FPC_DIR"<br />
mkdir -p etc<br />
bin/fpcmkcfg -d basepath="$FPC_DIR" -o etc/fpc.cfg # note 8<br />
<br />
cd bin<br />
echo "#!/bin/sh" > fpc.bash # note 9<br />
echo "# A script that ensure when we start fpc here, it ignores all other fpc.cfg" >> fpc.bash<br />
echo "$FPC_DIR""/bin/fpc -n @$FPC_DIR/etc/fpc.cfg \"\$@\"" >> fpc.bash<br />
chmod u+x fpc.bash<br />
<br />
<br />
* note 1 - right now (March 2024) fpc main thinks of it self as 3.3.1, this may change after the next release of FPC and its possible the necessary updates to make this all work will not be in that release.<br />
<br />
* note 2 - make sure you have that fpc.zip file there, FPC main Dec 2024 or later. cd ~/Downloads; wget https://gitlab.com/freepascal.org/fpc/source/-/archive/main/source-main.zip<br />
<br />
* note 3 - this takes a while.<br />
<br />
* note 4 - this takes a while too. And note it places the RTL files in a different place than fpcupdeluxe.<br />
<br />
* note 5 - this is your host system compiler.<br />
<br />
* note 6 - and this is the cross compiler, we call fpc and it calls this one.<br />
<br />
* note 7 - these are some units that the RTL, built earlier.<br />
<br />
* note 8 - this creates a pretty stock standard fpc.cfg file. <br />
<br />
* note 9 - this makes a script that calls fpc and passes parameters that ensure fpc takes no regard of any other fpc.cfg file, only the one we just made. The fpc.cfg that controls here is the one in note 8.<br />
<br />
{{Note | Whats different from fpcupdeluxe ? This model puts the Run Time Libraries in $FPC_DIR/units/arm-embedded/armv6m whereas fpcupdeluxe puts them in a more correct $FPC_DIR/units/arm-embedded/armv6m/eabi/rtl. The problem with the fpcupdeluxe model is that fpc does not look there by default, a patch to fpc.cfg is needed. As this is intended as a simple demo, I decided to keep it as simple as possible. If you prefer, change the UNITDIR variable to "$FPC_DIR"/units/arm-embedded/armv6m and patch your fpc.cfg to include in its search path. }}<br />
<br />
===Setup a Lazarus project===<br />
<br />
Doing your Pico development in Lazarus does make things simpler once setup. You can use (some of) Lazarus's help and code tools systems, click compile (but obviously not run) and have all your errors nicely presented. You do not need to make any changes to Lazarus (apart from settings mentioned below), just about any recent Lazarus will do. But, as mentioned above, makes sense to dedicate an install to Pico because of an unusual setting, next papa. <br />
<br />
Start a new Lazarus project in the usual way, to keep life simple, save it in a directory under 'projects' in Michael's directory tree. <br />
Firstly, a one off for this Lazarus instance, note this is NOT saved with the project. Open Options -> Files and look for the "Compiler executable" entry. Put in, with a full path, 'fpc.bash'. eg /home/YOUR-USER-NAME/bin/FPC/fpc-3.3.1/bin/fpc.bash (note fpcupdeluxe uses fpc.sh)<br />
<br />
Then, open the Project Options and set the following -<br />
<br />
Paths<br />
Other unit files (-Fu): ../pico-fpcexamples/units<br />
Include files (-Fi) : $(ProjOutDir)<br />
Libraries (-Fl);<br />
Unit output directory (-FU): lib/$(TargetCPU)-$(TargetOS)<br />
Target file name (-o) : bin/project1<br />
[x] Apply conventions<br />
<br />
Config and Target<br />
[x] Use standard compiler config...<br />
[ ] Use additional<br />
Target OS (-T) : Embedded<br />
Target CPU family (-P) : arm<br />
Target processor (-Cp) : ARMV6M<br />
<br />
Custom Options<br />
-Wpraspi_pico<br />
-godwarfsets<br />
-godwarfcpp<br />
-Xu<br />
-XParm-none-eabi-<br />
<br />
Note the last of the Custom Options, when you installed binutils-arm-none-eabi, you added a set of binutils for this particular processor. Some systems may call them arm-embedded-??? if yours uses that name scheme, change that Option accordingly.<br />
<br />
===Compile from Commandline===<br />
Its also reasonable to build from the command line, here is an example -<br />
<br />
#!/bin/bash<br />
echo "------- Using the fpc from 3.3.1 ---------"<br />
export MICHAELS_UNITS="$HOME"/Pico2/projects/pico-fpcexamples/units <br />
"$HOME"/bin/FPC/fpc-3.3.1/bin/fpc.bash -va -Xu -XParm-none-eabi- -Tembedded -Parm -CpARMV6M -Fu"$MICHAELS_UNITS" -Wpraspi_pico project1<br />
<br />
Note: -XParm-none-eabi- indicates how the binutils are named. If yours are named differently, change it !<br />
<br />
===See also===<br />
<br />
* [[ARM Embedded Tutorial - FPC and the Raspberry Pi Pico|ARM Embedded Raspberry Pi Pico Tutorials]]<br />
<br />
<br />
[[Category:Operating Systems and Platforms]]<br />
[[Category:ARM]]<br />
[[Category:Microcontrollers]]<br />
[[Category:Embedded]]<br />
[[Category:Tutorials]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Building_a_Pico_Compiler&diff=158394Building a Pico Compiler2024-03-10T09:57:28Z<p>Dbannon: additional note about how fpcupdeluxe works differently</p>
<hr />
<div>=Building a Pico Compiler=<br />
<br />
One of FPC's great features is its ability to provide cross compilers. A cross compiler allows you to run on one system and make a binary for another, possibly completely different system. When making binaries for embedded targets, its the only way to go !<br />
<br />
=== fpcupdeluxe ===<br />
For most users, the most convenient way to get a cross compiler for the Raspberry Pi Pico is to use [https://github.com/LongDirtyAnimAlf/fpcupdeluxe fpcupdeluxe] to do it all. It will install just FPC or FPC and Lazarus for you. Note it installs the 'main' version of each, from time to time, one or the other may have bugs (thats the nature of development branches) but not often. Try again in a day or so if you have problems. <br />
<br />
But this page is really for those who want to build their own, or just understand the process. Maybe adapt it to another CPU. These instructions focus on using a 64bit Linux system as the host, Windows and perhaps Mac users can probably do something similar.<br />
<br />
Much more details at [[ARM Embedded Tutorial - Installing Lazarus and Free Pascal]]<br />
<br />
{{Note| and this is important, this recipe produces a different file layout than fpupdeluxe. Its about simplicity not supporting a number of other Arm varieties for example. Do not mix this with a fpcupdeluxe or results may be unpredictable.}}<br />
<br />
===The constraints===<br />
* If you are a Lazarus user, it makes sense to have a stand alone Lazarus install for your Pico work, you could switch a single install (by changing Tools->Options->Files->Compiler_executable) but it could get annoying. Multiple installs means installing Lazarus from source, remarkably easy.<br />
* If you already have a Source install of FPC main December 2023 or later, you can use that, with some care. Again, might be easier and safer to have a dedicated version. You do need an existing install of FPC322, if yours is installed in read only space (installed from a deb or rpm) then I suggest you work here with with a fresh copy of FPC and "install" it into your own space. <br />
<br />
===Dependencies===<br />
* An existing, working, FPC322. <br />
* Install binutils-arm-none-eabi - that means ones for arm, no operating system (or embedded) and eabi indicates the arm subtype, no 'hf' means 'soft float'. <br />
* And, of course, you need Michael's git repo with pico-fpcexamples. These come from https://github.com/michael-ring/pico-fpcexamples Note that Michael's repo contain tow separate things, some nicely set example projects and, in 'units' a number of libraries (*.obj) and pascal units to link them into a pascal project.<br />
<br />
===The Process===<br />
Here are a series of command lines, I do not recommend you run them as a script (although I have) because you MUST check for errors at each step. I keep this in my home directory in a structure like this, change it of course but change the commands if you do ! The commands here will create two fpc-3.3.1 directories, if you have them already, ensure they are empty or change the commands. Note that I have already downloaded fpc.zip to my Downloads dir.<br />
<br />
===Assumed Dir Structure===<br />
$HOME/bin/FPC<br />
fpc-3.2.2 .....<br />
fpc-3.3.1<br />
SRC<br />
fpc-3.2.2 ....<br />
fpc-3.3.1<br />
<br />
=====The commands=====<br />
<br />
#!/bin/bash<br />
export FPC_VER="fpc-3.3.1" # note 1<br />
export SRC_DIR="$HOME/testFPC/FPC/SRC/$FPC_VER"<br />
export FPC_DIR="$HOME/testFPC/FPC/$FPC_VER"<br />
UNITDIR="$FPC_DIR"/units/arm-embedded/armv6m<br />
<br />
mkdir -p "$FPC_DIR"<br />
mkdir -p "$SRC_DIR"<br />
cd "$SRC_DIR"<br />
unzip ~/Downloads/fpc.zip # note 2<br />
cd fpc<br />
make all # note 3<br />
make install INSTALL_PREFIX="$FPC_DIR"<br />
<br />
# note 4<br />
make crossinstall CPU_TARGET=arm OS_TARGET=embedded SUBARCH=armv6m CROSSBINDIR=/usr/bin BINUTILSPREFIX=arm-none-eabi- CROSSOPT="-CpARMV6M -CaEABI" INSTALL_PREFIX="$FPC_DIR" INSTALL_UNITDIR="$UNITDIR"<br />
<br />
cp compiler/ppcx64 "$FPC_DIR"/bin/ppcx64 # note 5<br />
cp compiler/ppcrossarm "$FPC_DIR"/bin/ppcrossarm # note 6<br />
<br />
cp rtl/embedded/arm/* "$UNITDIR" # note 7<br />
<br />
cd "$FPC_DIR"<br />
mkdir -p etc<br />
bin/fpcmkcfg -d basepath="$FPC_DIR" -o etc/fpc.cfg # note 8<br />
<br />
cd bin<br />
echo "#!/bin/sh" > fpc.bash # note 9<br />
echo "# A script that ensure when we start fpc here, it ignores all other fpc.cfg" >> fpc.bash<br />
echo "$FPC_DIR""/bin/fpc -n @$FPC_DIR/etc/fpc.cfg \"\$@\"" >> fpc.bash<br />
chmod u+x fpc.bash<br />
<br />
<br />
* note 1 - right now (March 2024) fpc main thinks of it self as 3.3.1, this may change after the next release of FPC and its possible the necessary updates to make this all work will not be in that release.<br />
<br />
* note 2 - make sure you have that fpc.zip file there, FPC main Dec 2024 or later. cd ~/Downloads; wget https://gitlab.com/freepascal.org/fpc/source/-/archive/main/source-main.zip<br />
<br />
* note 3 - this takes a while.<br />
<br />
* note 4 - this takes a while too. And note it places the RTL files in a different place than fpcupdeluxe.<br />
<br />
* note 5 - this is your host system compiler.<br />
<br />
* note 6 - and this is the cross compiler, we call fpc and it calls this one.<br />
<br />
* note 7 - these are some units that the RTL, built earlier.<br />
<br />
* note 8 - this creates a pretty stock standard fpc.cfg file. <br />
<br />
* note 9 - this makes a script that calls fpc and passes parameters that ensure fpc takes no regard of any other fpc.cfg file, only the one we just made. The fpc.cfg that controls here is the one in note 8.<br />
<br />
{{Note | Whats different from fpcupdeluxe ? This model puts the Run Time Libraries in $FPC_DIR/units/arm-embedded/armv6m whereas fpcupdeluxe puts them in a more correct $FPC_DIR/units/arm-embedded/armv6m/eabi/rtl. The problem with the fpcupdeluxe model is that fpc does not look there by default, a patch to fpc.cfg is needed. As this is intended as a simple demo, I decided to keep it as simple as possible. If you prefer, change the UNITDIR variable to "$FPC_DIR"/units/arm-embedded/armv6m and patch your fpc.cfg to include in its search path. }}<br />
<br />
===Setup a Lazarus project===<br />
<br />
Doing your Pico development in Lazarus does make things simpler once setup. You can use (some of) Lazarus's help and code tools systems, click compile (but obviously not run) and have all your errors nicely presented. You do not need to make any changes to Lazarus (apart from settings mentioned below), just about any recent Lazarus will do. But, as mentioned above, makes sense to dedicate an install to Pico because of an unusual setting, next papa. <br />
<br />
Start a new Lazarus project in the usual way, to keep life simple, save it in a directory under 'projects' in Michael's directory tree. <br />
Firstly, a one off for this Lazarus instance, note this is NOT saved with the project. Open Options -> Files and look for the "Compiler executable" entry. Put in, with a full path, 'fpc.bash'. eg /home/YOUR-USER-NAME/bin/FPC/fpc-3.3.1/bin/fpc.bash (note fpcupdeluxe uses fpc.sh)<br />
<br />
Then, open the Project Options and set the following -<br />
<br />
Paths<br />
Other unit files (-Fu): ../pico-fpcexamples/units<br />
Include files (-Fi) : $(ProjOutDir)<br />
Libraries (-Fl);<br />
Unit output directory (-FU): lib/$(TargetCPU)-$(TargetOS)<br />
Target file name (-o) : bin/project1<br />
[x] Apply conventions<br />
<br />
Config and Target<br />
[x] Use standard compiler config...<br />
[ ] Use additional<br />
Target OS (-T) : Embedded<br />
Target CPU family (-P) : arm<br />
Target processor (-Cp) : ARMV6M<br />
<br />
Custom Options<br />
-Wpraspi_pico<br />
-godwarfsets<br />
-godwarfcpp<br />
-Xu<br />
-XParm-none-eabi-<br />
<br />
Note the last of the Custom Options, when you installed binutils-arm-none-eabi, you added a set of binutils for this particular processor. Some systems may call them arm-embedded-??? if yours uses that name scheme, change that Option accordingly.<br />
<br />
===Compile from Commandline===<br />
Its also reasonable to build from the command line, here is an example -<br />
<br />
#!/bin/bash<br />
echo "------- Using the fpc from 3.3.1 ---------"<br />
export MICHAELS_UNITS="$HOME"/Pico2/projects/pico-fpcexamples/units <br />
"$HOME"/bin/FPC/fpc-3.3.1/bin/fpc.bash -va -Xu -XParm-none-eabi- -Tembedded -Parm -CpARMV6M -Fu"$MICHAELS_UNITS" -Wpraspi_pico project1<br />
<br />
Note: -XParm-none-eabi- indicates how the binutils are named. If yours are named differently, change it !<br />
<br />
===See also===<br />
<br />
* [[ARM Embedded Tutorial - FPC and the Raspberry Pi Pico|ARM Embedded Raspberry Pi Pico Tutorials]]<br />
<br />
<br />
[[Category:Operating Systems and Platforms]]<br />
[[Category:ARM]]<br />
[[Category:Microcontrollers]]<br />
[[Category:Embedded]]<br />
[[Category:Tutorials]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Building_a_Pico_Compiler&diff=158393Building a Pico Compiler2024-03-10T09:53:36Z<p>Dbannon: revised script</p>
<hr />
<div>=Building a Pico Compiler=<br />
<br />
One of FPC's great features is its ability to provide cross compilers. A cross compiler allows you to run on one system and make a binary for another, possibly completely different system. When making binaries for embedded targets, its the only way to go !<br />
<br />
=== fpcupdeluxe ===<br />
For most users, the most convenient way to get a cross compiler for the Raspberry Pi Pico is to use [https://github.com/LongDirtyAnimAlf/fpcupdeluxe fpcupdeluxe] to do it all. It will install just FPC or FPC and Lazarus for you. Note it installs the 'main' version of each, from time to time, one or the other may have bugs (thats the nature of development branches) but not often. Try again in a day or so if you have problems. <br />
<br />
But this page is really for those who want to build their own, or just understand the process. Maybe adapt it to another CPU. These instructions focus on using a 64bit Linux system as the host, Windows and perhaps Mac users can probably do something similar.<br />
<br />
Much more details at [[ARM Embedded Tutorial - Installing Lazarus and Free Pascal]]<br />
<br />
{{Note| and this is important, this recipe produces a different file layout than fpupdeluxe. Its about simplicity not supporting a number of other Arm varieties for example. Do not mix this with a fpcupdeluxe or results may be unpredictable.}}<br />
<br />
===The constraints===<br />
* If you are a Lazarus user, it makes sense to have a stand alone Lazarus install for your Pico work, you could switch a single install (by changing Tools->Options->Files->Compiler_executable) but it could get annoying. Multiple installs means installing Lazarus from source, remarkably easy.<br />
* If you already have a Source install of FPC main December 2023 or later, you can use that, with some care. Again, might be easier and safer to have a dedicated version. You do need an existing install of FPC322, if yours is installed in read only space (installed from a deb or rpm) then I suggest you work here with with a fresh copy of FPC and "install" it into your own space. <br />
<br />
===Dependencies===<br />
* An existing, working, FPC322. <br />
* Install binutils-arm-none-eabi - that means ones for arm, no operating system (or embedded) and eabi indicates the arm subtype, no 'hf' means 'soft float'. <br />
* And, of course, you need Michael's git repo with pico-fpcexamples. These come from https://github.com/michael-ring/pico-fpcexamples Note that Michael's repo contain tow separate things, some nicely set example projects and, in 'units' a number of libraries (*.obj) and pascal units to link them into a pascal project.<br />
<br />
===The Process===<br />
Here are a series of command lines, I do not recommend you run them as a script (although I have) because you MUST check for errors at each step. I keep this in my home directory in a structure like this, change it of course but change the commands if you do ! The commands here will create two fpc-3.3.1 directories, if you have them already, ensure they are empty or change the commands. Note that I have already downloaded fpc.zip to my Downloads dir.<br />
<br />
===Assumed Dir Structure===<br />
$HOME/bin/FPC<br />
fpc-3.2.2 .....<br />
fpc-3.3.1<br />
SRC<br />
fpc-3.2.2 ....<br />
fpc-3.3.1<br />
<br />
=====The commands=====<br />
<br />
#!/bin/bash<br />
export FPC_VER="fpc-3.3.1" # note 1<br />
export SRC_DIR="$HOME/testFPC/FPC/SRC/$FPC_VER"<br />
export FPC_DIR="$HOME/testFPC/FPC/$FPC_VER"<br />
UNITDIR="$FPC_DIR"/units/arm-embedded/armv6m<br />
<br />
mkdir -p "$FPC_DIR"<br />
mkdir -p "$SRC_DIR"<br />
cd "$SRC_DIR"<br />
unzip ~/Downloads/fpc.zip # note 2<br />
cd fpc<br />
make all # note 3<br />
make install INSTALL_PREFIX="$FPC_DIR"<br />
<br />
# note 4<br />
make crossinstall CPU_TARGET=arm OS_TARGET=embedded SUBARCH=armv6m CROSSBINDIR=/usr/bin BINUTILSPREFIX=arm-none-eabi- CROSSOPT="-CpARMV6M -CaEABI" INSTALL_PREFIX="$FPC_DIR" INSTALL_UNITDIR="$UNITDIR"<br />
<br />
cp compiler/ppcx64 "$FPC_DIR"/bin/ppcx64 # note 5<br />
cp compiler/ppcrossarm "$FPC_DIR"/bin/ppcrossarm # note 6<br />
<br />
cp rtl/embedded/arm/* "$UNITDIR" # note 7<br />
<br />
cd "$FPC_DIR"<br />
mkdir -p etc<br />
bin/fpcmkcfg -d basepath="$FPC_DIR" -o etc/fpc.cfg # note 8<br />
<br />
cd bin<br />
echo "#!/bin/sh" > fpc.bash # note 9<br />
echo "# A script that ensure when we start fpc here, it ignores all other fpc.cfg" >> fpc.bash<br />
echo "$FPC_DIR""/bin/fpc -n @$FPC_DIR/etc/fpc.cfg \"\$@\"" >> fpc.bash<br />
chmod u+x fpc.bash<br />
<br />
<br />
* note 1 - right now (March 2024) fpc main thinks of it self as 3.3.1, this may change after the next release of FPC and its possible the necessary updates to make this all work will not be in that release.<br />
<br />
* note 2 - make sure you have that fpc.zip file there, FPC main Dec 2024 or later. cd ~/Downloads; wget https://gitlab.com/freepascal.org/fpc/source/-/archive/main/source-main.zip<br />
<br />
* note 3 - this takes a while.<br />
<br />
* note 4 - this takes a while too. And note it places the RTL files in a different place than fpcupdeluxe.<br />
<br />
* note 5 - this is your host system compiler.<br />
<br />
* note 6 - and this is the cross compiler, we call fpc and it calls this one.<br />
<br />
* note 7 - these are some units that the RTL, built earlier.<br />
<br />
* note 8 - this creates a pretty stock standard fpc.cfg file. <br />
<br />
* note 9 - this makes a script that calls fpc and passes parameters that ensure fpc takes no regard of any other fpc.cfg file, only the one we just made. The fpc.cfg that controls here is the one in note 8.<br />
<br />
{{Note | Whats different from fpcupdeluxe ? This model puts the Run Time Libraries in $FPC_DIR/units/arm-embedded/armv6m whereas fpcupdeluxe puts them in a more correct $FPC_DIR/units/arm-embedded/armv6m/eabi/rtl. The problem with the fpcupdeluxe model is that fpc does not look there by default, a patch to fpc.cfg is needed. As this is intended as a simple demo, I decided to keep it as simple as possible.}}<br />
<br />
===Setup a Lazarus project===<br />
<br />
Doing your Pico development in Lazarus does make things simpler once setup. You can use (some of) Lazarus's help and code tools systems, click compile (but obviously not run) and have all your errors nicely presented. You do not need to make any changes to Lazarus (apart from settings mentioned below), just about any recent Lazarus will do. But, as mentioned above, makes sense to dedicate an install to Pico because of an unusual setting, next papa. <br />
<br />
Start a new Lazarus project in the usual way, to keep life simple, save it in a directory under 'projects' in Michael's directory tree. <br />
Firstly, a one off for this Lazarus instance, note this is NOT saved with the project. Open Options -> Files and look for the "Compiler executable" entry. Put in, with a full path, 'fpc.bash'. eg /home/YOUR-USER-NAME/bin/FPC/fpc-3.3.1/bin/fpc.bash (note fpcupdeluxe uses fpc.sh)<br />
<br />
Then, open the Project Options and set the following -<br />
<br />
Paths<br />
Other unit files (-Fu): ../pico-fpcexamples/units<br />
Include files (-Fi) : $(ProjOutDir)<br />
Libraries (-Fl);<br />
Unit output directory (-FU): lib/$(TargetCPU)-$(TargetOS)<br />
Target file name (-o) : bin/project1<br />
[x] Apply conventions<br />
<br />
Config and Target<br />
[x] Use standard compiler config...<br />
[ ] Use additional<br />
Target OS (-T) : Embedded<br />
Target CPU family (-P) : arm<br />
Target processor (-Cp) : ARMV6M<br />
<br />
Custom Options<br />
-Wpraspi_pico<br />
-godwarfsets<br />
-godwarfcpp<br />
-Xu<br />
-XParm-none-eabi-<br />
<br />
Note the last of the Custom Options, when you installed binutils-arm-none-eabi, you added a set of binutils for this particular processor. Some systems may call them arm-embedded-??? if yours uses that name scheme, change that Option accordingly.<br />
<br />
===Compile from Commandline===<br />
Its also reasonable to build from the command line, here is an example -<br />
<br />
#!/bin/bash<br />
echo "------- Using the fpc from 3.3.1 ---------"<br />
export MICHAELS_UNITS="$HOME"/Pico2/projects/pico-fpcexamples/units <br />
"$HOME"/bin/FPC/fpc-3.3.1/bin/fpc.bash -va -Xu -XParm-none-eabi- -Tembedded -Parm -CpARMV6M -Fu"$MICHAELS_UNITS" -Wpraspi_pico project1<br />
<br />
Note: -XParm-none-eabi- indicates how the binutils are named. If yours are named differently, change it !<br />
<br />
===See also===<br />
<br />
* [[ARM Embedded Tutorial - FPC and the Raspberry Pi Pico|ARM Embedded Raspberry Pi Pico Tutorials]]<br />
<br />
<br />
[[Category:Operating Systems and Platforms]]<br />
[[Category:ARM]]<br />
[[Category:Microcontrollers]]<br />
[[Category:Embedded]]<br />
[[Category:Tutorials]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Building_a_Pico_Compiler&diff=158392Building a Pico Compiler2024-03-10T09:46:11Z<p>Dbannon: revised script</p>
<hr />
<div>=Building a Pico Compiler=<br />
<br />
One of FPC's great features is its ability to provide cross compilers. A cross compiler allows you to run on one system and make a binary for another, possibly completely different system. When making binaries for embedded targets, its the only way to go !<br />
<br />
=== fpcupdeluxe ===<br />
For most users, the most convenient way to get a cross compiler for the Raspberry Pi Pico is to use [https://github.com/LongDirtyAnimAlf/fpcupdeluxe fpcupdeluxe] to do it all. It will install just FPC or FPC and Lazarus for you. Note it installs the 'main' version of each, from time to time, one or the other may have bugs (thats the nature of development branches) but not often. Try again in a day or so if you have problems. <br />
<br />
But this page is really for those who want to build their own, or just understand the process. Maybe adapt it to another CPU. These instructions focus on using a 64bit Linux system as the host, Windows and perhaps Mac users can probably do something similar.<br />
<br />
Much more details at [[ARM Embedded Tutorial - Installing Lazarus and Free Pascal]]<br />
<br />
{{Note| and this is important, this recipe produces a different file layout than fpupdeluxe. Its about simplicity not supporting a number of other Arm varieties for example. Do not mix this with a fpcupdeluxe or results may be unpredictable.}}<br />
<br />
===The constraints===<br />
* If you are a Lazarus user, it makes sense to have a stand alone Lazarus install for your Pico work, you could switch a single install (by changing Tools->Options->Files->Compiler_executable) but it could get annoying. Multiple installs means installing Lazarus from source, remarkably easy.<br />
* If you already have a Source install of FPC main December 2023 or later, you can use that, with some care. Again, might be easier and safer to have a dedicated version. You do need an existing install of FPC322, if yours is installed in read only space (installed from a deb or rpm) then I suggest you work here with with a fresh copy of FPC and "install" it into your own space. <br />
<br />
===Dependencies===<br />
* An existing, working, FPC322. <br />
* Install binutils-arm-none-eabi - that means ones for arm, no operating system (or embedded) and eabi indicates the arm subtype, no 'hf' means 'soft float'. <br />
* And, of course, you need Michael's git repo with pico-fpcexamples. These come from https://github.com/michael-ring/pico-fpcexamples Note that Michael's repo contain tow separate things, some nicely set example projects and, in 'units' a number of libraries (*.obj) and pascal units to link them into a pascal project.<br />
<br />
===The Process===<br />
Here are a series of command lines, I do not recommend you run them as a script (although I have) because you MUST check for errors at each step. I keep this in my home directory in a structure like this, change it of course but change the commands if you do ! The commands here will create two fpc-3.3.1 directories, if you have them already, ensure they are empty or change the commands. Note that I have already downloaded fpc.zip to my Downloads dir.<br />
<br />
===Assumed Dir Structure===<br />
$HOME/bin/FPC<br />
fpc-3.2.2 .....<br />
fpc-3.3.1<br />
SRC<br />
fpc-3.2.2 ....<br />
fpc-3.3.1<br />
<br />
=====The commands=====<br />
<br />
#!/bin/bash<br />
export FPC_VER="fpc-3.3.1" # note 1<br />
export SRC_DIR="$HOME/testFPC/FPC/SRC/$FPC_VER"<br />
export FPC_DIR="$HOME/testFPC/FPC/$FPC_VER"<br />
UNITDIR="$FPC_DIR"/units/arm-embedded/armv6m<br />
<br />
mkdir -p "$FPC_DIR"<br />
mkdir -p "$SRC_DIR"<br />
cd "$SRC_DIR"<br />
unzip ~/Downloads/fpc.zip # note 2<br />
cd fpc<br />
make all # note 3<br />
make install INSTALL_PREFIX="$FPC_DIR"<br />
<br />
# note 4<br />
make crossinstall CPU_TARGET=arm OS_TARGET=embedded SUBARCH=armv6m CROSSBINDIR=/usr/bin BINUTILSPREFIX=arm-none-eabi- CROSSOPT="-CpARMV6M -CaEABI" INSTALL_PREFIX="$FPC_DIR" INSTALL_UNITDIR="$UNITDIR"<br />
<br />
cp compiler/ppcx64 "$FPC_DIR"/bin/ppcx64 # note 5<br />
cp compiler/ppcrossarm "$FPC_DIR"/bin/ppcrossarm # note 6<br />
<br />
cp rtl/embedded/arm/* "$UNITDIR" # note 7<br />
<br />
cd "$FPC_DIR"<br />
mkdir -p etc<br />
bin/fpcmkcfg -d basepath="$FPC_DIR" -o etc/fpc.cfg # note 8<br />
<br />
cd bin<br />
echo "#!/bin/sh" > fpc.bash # note 9<br />
echo "# A script that ensure when we start fpc here, it ignores all other fpc.cfg" >> fpc.bash<br />
echo "$FPC_DIR""/bin/fpc -n @$FPC_DIR/etc/fpc.cfg \"\$@\"" >> fpc.bash<br />
chmod u+x fpc.bash<br />
<br />
<br />
* note 1 - right now (March 2024) fpc main thinks of it self as 3.3.1, this may change after the next release of FPC and its possible the necessary updates to make this all work will not be in that release.<br />
<br />
* note 2 - make sure you have that fpc.zip file there, FPC main Dec 2024 or later.<br />
<br />
* note 3 - this takes a while.<br />
<br />
* note 4 - this takes a while too. And note it places the RTL files in a different place than fpcupdeluxe.<br />
<br />
* note 5 - this is your host system compiler.<br />
<br />
* note 6 - and this is the cross compiler, we call fpc and it calls this one.<br />
<br />
* note 7 - these are some units that the RTL, built earlier.<br />
<br />
* note 8 - this creates a pretty stock standard fpc.cfg file. <br />
<br />
* note 9 - this makes a script that calls fpc and passes parameters that ensure fpc takes no regard of any other fpc.cfg file, only the one we just made. The fpc.cfg that controls here is the one in note 8.<br />
<br />
{{Note | Whats different from fpcupdeluxe ? This model puts the Run Time Libraries in $FPC_DIR/units/arm-embedded/armv6m whereas fpcupdeluxe puts them in a more correct $FPC_DIR/units/arm-embedded/armv6m/eabi/rtl. The problem with the fpcupdeluxe model is that fpc does not look there by default, a patch to fpc.cfg is needed. As this is intended as a simple demo, I decided to keep it as simple as possible.}}<br />
<br />
===Setup a Lazarus project===<br />
<br />
Doing your Pico development in Lazarus does make things simpler once setup. You can use (some of) Lazarus's help and code tools systems, click compile (but obviously not run) and have all your errors nicely presented. You do not need to make any changes to Lazarus (apart from settings mentioned below), just about any recent Lazarus will do. But, as mentioned above, makes sense to dedicate an install to Pico because of an unusual setting, next papa. <br />
<br />
Start a new Lazarus project in the usual way, to keep life simple, save it in a directory under 'projects' in Michael's directory tree. <br />
Firstly, a one off for this Lazarus instance, note this is NOT saved with the project. Open Options -> Files and look for the "Compiler executable" entry. Put in, with a full path, 'fpc.bash'. eg /home/YOUR-USER-NAME/bin/FPC/fpc-3.3.1/bin/fpc.bash (note fpcupdeluxe uses fpc.sh)<br />
<br />
Then, open the Project Options and set the following -<br />
<br />
Paths<br />
Other unit files (-Fu): ../pico-fpcexamples/units<br />
Include files (-Fi) : $(ProjOutDir)<br />
Libraries (-Fl);<br />
Unit output directory (-FU): lib/$(TargetCPU)-$(TargetOS)<br />
Target file name (-o) : bin/project1<br />
[x] Apply conventions<br />
<br />
Config and Target<br />
[x] Use standard compiler config...<br />
[ ] Use additional<br />
Target OS (-T) : Embedded<br />
Target CPU family (-P) : arm<br />
Target processor (-Cp) : ARMV6M<br />
<br />
Custom Options<br />
-Wpraspi_pico<br />
-godwarfsets<br />
-godwarfcpp<br />
-Xu<br />
-XParm-none-eabi-<br />
<br />
Note the last of the Custom Options, when you installed binutils-arm-none-eabi, you added a set of binutils for this particular processor. Some systems may call them arm-embedded-??? if yours uses that name scheme, change that Option accordingly.<br />
<br />
===Compile from Commandline===<br />
Its also reasonable to build from the command line, here is an example -<br />
<br />
#!/bin/bash<br />
echo "------- Using the fpc from 3.3.1 ---------"<br />
export MICHAELS_UNITS="$HOME"/Pico2/projects/pico-fpcexamples/units <br />
"$HOME"/bin/FPC/fpc-3.3.1/bin/fpc.bash -va -Xu -XParm-none-eabi- -Tembedded -Parm -CpARMV6M -Fu"$MICHAELS_UNITS" -Wpraspi_pico project1<br />
<br />
Note: -XParm-none-eabi- indicates how the binutils are named. If yours are named differently, change it !<br />
<br />
===See also===<br />
<br />
* [[ARM Embedded Tutorial - FPC and the Raspberry Pi Pico|ARM Embedded Raspberry Pi Pico Tutorials]]<br />
<br />
<br />
[[Category:Operating Systems and Platforms]]<br />
[[Category:ARM]]<br />
[[Category:Microcontrollers]]<br />
[[Category:Embedded]]<br />
[[Category:Tutorials]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Building_a_Pico_Compiler&diff=158389Building a Pico Compiler2024-03-09T09:20:41Z<p>Dbannon: /* fpcupdeluxe */</p>
<hr />
<div>=Building a Pico Compiler=<br />
<br />
One of FPC's great features is its ability to provide cross compilers. A cross compiler allows you to run on one system and make a binary for another, possibly completely different system. When making binaries for embedded targets, its the only way to go !<br />
<br />
=== fpcupdeluxe ===<br />
For most users, the most convenient way to get a cross compiler for the Raspberry Pi Pico is to use [https://github.com/LongDirtyAnimAlf/fpcupdeluxe fpcupdeluxe] to do it all. It will install just FPC or FPC and Lazarus for you. Note it installs the 'main' version of each, from time to time, one or the other may have bugs (thats the nature of development branches) but not often. Try again in a day or so if you have problems. <br />
<br />
But this page is really for those who want to build their own, or just understand the process. Maybe adapt it to another CPU. These instructions focus on using a 64bit Linux system as the host, Windows and perhaps Mac users can probably do something similar.<br />
<br />
Much more details at [[ARM Embedded Tutorial - Installing Lazarus and Free Pascal]]<br />
<br />
{{Note| and this is important, this recipe produces a different file layout than fpupdeluxe. Its about simplicity not supporting a number of other Arm varieties for example. Do not mix this with a fpcupdeluxe or results may be unpredictable.}}<br />
<br />
===The constraints===<br />
* If you are a Lazarus user, it makes sense to have a stand alone Lazarus install for your Pico work, you could switch a single install (by changing Tools->Options->Files->Compiler_executable) but it could get annoying. Multiple installs means installing Lazarus from source, remarkably easy.<br />
* If you already have a Source install of FPC main December 2023 or later, you can use that, with some care. Again, might be easier and safer to have a dedicated version. You do need an existing install of FPC322, if yours is installed in read only space (installed from a deb or rpm) then I suggest you work here with with a fresh copy of FPC and "install" it into your own space. <br />
<br />
===Dependencies===<br />
* An existing, working, FPC322. <br />
* Install binutils-arm-none-eabi - that means ones for arm, no operating system (or embedded) and eabi indicates the arm subtype, no 'hf' means 'soft float'. <br />
* And, of course, you need Michael's git repo with pico-fpcexamples. These come from https://github.com/michael-ring/pico-fpcexamples Note that Michael's repo contain tow separate things, some nicely set example projects and, in 'units' a number of libraries (*.obj) and pascal units to link them into a pascal project.<br />
<br />
===The Process===<br />
Here are a series of command lines, I do not recommend you run them as a script (although I have) because you MUST check for errors at each step. I keep this in my home directory in a structure like this, change it of course but change the commands if you do ! The commands here will create two fpc-3.3.1 directories, if you have them already, ensure they are empty or change the commands. Note that I have already downloaded fpc.zip to my Downloads dir.<br />
<br />
===Assumed Dir Structure===<br />
$HOME/bin/FPC<br />
fpc-3.2.2 .....<br />
fpc-3.3.1<br />
SRC<br />
fpc-3.2.2 ....<br />
fpc-3.3.1<br />
<br />
=====The commands=====<br />
<br />
#!/bin/bash<br />
<br />
export FPC_VER="fpc-3.3.1" # note 1<br />
export SRC_DIR="$HOME/bin/FPC/SRC/$FPC_VER"<br />
export FPC_DIR="$HOME/bin/FPC/$FPC_VER"<br />
mkdir -p "$FPC_DIR"<br />
mkdir -p "$SRC_DIR"<br />
<br />
cd "$SRC_DIR"<br />
unzip ~/Downloads/fpc.zip # note 2<br />
cd fpc<br />
make all # note 3<br />
<br />
make install INSTALL_PREFIX="$FPC_DIR"<br />
<br />
# note 4<br />
make crossinstall CPU_TARGET=arm OS_TARGET=embedded SUBARCH=armv6m CROSSBINDIR=/usr/bin BINUTILSPREFIX=arm-none-eabi- CROSSOPT="-CpARMV6M -CaEABI" INSTALL_PREFIX="$FPC_DIR"<br />
<br />
<br />
cp compiler/ppcx64 "$FPC_DIR"/bin/ppcx64 # note 5 <br />
cp compiler/ppcrossarm "$FPC_DIR"/bin/ppcrossarm # note 6<br />
<br />
# note 7<br />
cp rtl/embedded/arm/* "$FPC_DIR"/lib/fpc/3.3.1/units/arm-embedded/rtl/.<br />
<br />
cd "$FPC_DIR"<br />
mkdir -p etc<br />
bin/fpcmkcfg -d basepath="$FPC_DIR" -o etc/fpc.cfg # note 8<br />
ln -s "$FPC_DIR"/lib/fpc/3.3.1/units units # note 9<br />
<br />
<br />
cd bin<br />
echo "#!/bin/sh" > fpc.bash # note 10<br />
echo "# A script that ensure when we start fpc here, it ignores all other fpc.cfg" >> fpc.bash<br />
echo "$FPC_DIR""/bin/fpc -n @$FPC_DIR/etc/fpc.cfg \"\$@\"" >> fpc.bash<br />
chmod u+x fpc.bash<br />
<br />
* note 1 - right now (March 2024) fpc main thinks of it self as 3.3.1, this may change after the next release of FPC and its possible the necessary updates to make this all work will not be in the release.<br />
<br />
* note 2 - make sure you have that fpc.zip file there, main Dec 2024 or later.<br />
<br />
* note 3 - this takes a while.<br />
<br />
* note 4 - this takes a while too. And it places the RTL files in a different place than fpcupdeluxe.<br />
<br />
* note 5 - this is your host system compiler.<br />
<br />
* note 6 - and this is the cross compiler, we call fpc and it calls this one.<br />
<br />
* note 7 - these are some units that the RTL, built earlier, will need. Again, files end up in a different place than fpcupdeluxe uses. fpcupdeluxe uses ... arm-embedded/eabi/rtl/. We cannot support other Arm embedded systems here.<br />
<br />
* note 8 - this creates a pretty stock standard fpc.cfg file. <br />
<br />
* note 9 - we link the RTL files we made earlier to where the std config file expects them to be.<br />
<br />
* note 10 - this makes a script that calls fpc and passes parameters that ensure fpc takes no regard of any other fpc.cfg file, only the one we just made.<br />
<br />
===Setup a Lazarus project===<br />
<br />
Doing your Pico development in Lazarus does make things simpler once setup. You can use (some of) Lazarus's help and code tools systems, click compile (but obviously not run) and have all your errors nicely presented. You do not need to make any changes to Lazarus (apart from settings mentioned below), just about any recent Lazarus will do. But, as mentioned above, makes sense to dedicate an install to Pico because of an unusual setting, next papa. <br />
<br />
Start a new Lazarus project in the usual way, to keep life simple, save it in a directory under 'projects' in Michael's directory tree. <br />
Firstly, a one off for this Lazarus instance, note this is NOT saved with the project. Open Options -> Files and look for the "Compiler executable" entry. Put in, with a full path, 'fpc.bash'. eg /home/YOUR-USER-NAME/bin/FPC/fpc-3.3.1/bin/fpc.bash (note fpcupdeluxe uses fpc.sh)<br />
<br />
Then, open the Project Options and set the following -<br />
<br />
Paths<br />
Other unit files (-Fu): ../pico-fpcexamples/units<br />
Include files (-Fi) : $(ProjOutDir)<br />
Libraries (-Fl);<br />
Unit output directory (-FU): lib/$(TargetCPU)-$(TargetOS)<br />
Target file name (-o) : bin/project1<br />
[x] Apply conventions<br />
<br />
Config and Target<br />
[x] Use standard compiler config...<br />
[ ] Use additional<br />
Target OS (-T) : Embedded<br />
Target CPU family (-P) : arm<br />
Target processor (-Cp) : ARMV6M<br />
<br />
Custom Options<br />
-Wpraspi_pico<br />
-godwarfsets<br />
-godwarfcpp<br />
-Xu<br />
-XParm-none-eabi-<br />
<br />
Note the last of the Custom Options, when you installed binutils-arm-none-eabi, you added a set of binutils for this particular processor. Some systems may call them arm-embedded-??? if yours uses that name scheme, change that Option accordingly.<br />
<br />
===Compile from Commandline===<br />
Its also reasonable to build from the command line, here is an example -<br />
<br />
#!/bin/bash<br />
echo "------- Using the fpc from 3.3.1 ---------"<br />
export MICHAELS_UNITS="$HOME"/Pico2/projects/pico-fpcexamples/units <br />
"$HOME"/bin/FPC/fpc-3.3.1/bin/fpc.bash -va -Xu -XParm-none-eabi- -Tembedded -Parm -CpARMV6M -Fu"$MICHAELS_UNITS" -Wpraspi_pico project1<br />
<br />
Note: -XParm-none-eabi- indicates how the binutils are named. If yours are named differently, change it !<br />
<br />
===See also===<br />
<br />
* [[ARM Embedded Tutorial - FPC and the Raspberry Pi Pico|ARM Embedded Raspberry Pi Pico Tutorials]]<br />
<br />
<br />
[[Category:Operating Systems and Platforms]]<br />
[[Category:ARM]]<br />
[[Category:Microcontrollers]]<br />
[[Category:Embedded]]<br />
[[Category:Tutorials]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Building_a_Pico_Compiler&diff=158388Building a Pico Compiler2024-03-09T09:19:29Z<p>Dbannon: tweak</p>
<hr />
<div>=Building a Pico Compiler=<br />
<br />
One of FPC's great features is its ability to provide cross compilers. A cross compiler allows you to run on one system and make a binary for another, possibly completely different system. When making binaries for embedded targets, its the only way to go !<br />
<br />
=== fpcupdeluxe ===<br />
For most users, the most convenient way to get a cross compiler for the Raspberry Pi Pico is to use [https://github.com/LongDirtyAnimAlf/fpcupdeluxe fpcupdeluxe] to do it all. It will install just FPC or FPC and Lazarus for you. Note it installs the 'main' version of each, from time to time, one or the other may have bugs (thats the nature of development branches) but not often. Try again in a day or so if you have problems. <br />
<br />
But this page is really for those who want to build their own, or just understand the process. Maybe adapt it to another CPU. These instructions focus on using a 64bit Linux system as the host, Windows and perhaps Mac users can probably do something similar.<br />
<br />
Much more details at [[ARM Embedded Tutorial - Installing Lazarus and Free Pascal]]<br />
<br />
{{Note| and this is important, this recipes produces a different file layout that fpupdeluxe. Its about simplicity not supporting a number of other Arm varieties for example. Do not mix this with a fpcupdeluxe or results may be unpredictable.}}<br />
<br />
===The constraints===<br />
* If you are a Lazarus user, it makes sense to have a stand alone Lazarus install for your Pico work, you could switch a single install (by changing Tools->Options->Files->Compiler_executable) but it could get annoying. Multiple installs means installing Lazarus from source, remarkably easy.<br />
* If you already have a Source install of FPC main December 2023 or later, you can use that, with some care. Again, might be easier and safer to have a dedicated version. You do need an existing install of FPC322, if yours is installed in read only space (installed from a deb or rpm) then I suggest you work here with with a fresh copy of FPC and "install" it into your own space. <br />
<br />
===Dependencies===<br />
* An existing, working, FPC322. <br />
* Install binutils-arm-none-eabi - that means ones for arm, no operating system (or embedded) and eabi indicates the arm subtype, no 'hf' means 'soft float'. <br />
* And, of course, you need Michael's git repo with pico-fpcexamples. These come from https://github.com/michael-ring/pico-fpcexamples Note that Michael's repo contain tow separate things, some nicely set example projects and, in 'units' a number of libraries (*.obj) and pascal units to link them into a pascal project.<br />
<br />
===The Process===<br />
Here are a series of command lines, I do not recommend you run them as a script (although I have) because you MUST check for errors at each step. I keep this in my home directory in a structure like this, change it of course but change the commands if you do ! The commands here will create two fpc-3.3.1 directories, if you have them already, ensure they are empty or change the commands. Note that I have already downloaded fpc.zip to my Downloads dir.<br />
<br />
===Assumed Dir Structure===<br />
$HOME/bin/FPC<br />
fpc-3.2.2 .....<br />
fpc-3.3.1<br />
SRC<br />
fpc-3.2.2 ....<br />
fpc-3.3.1<br />
<br />
=====The commands=====<br />
<br />
#!/bin/bash<br />
<br />
export FPC_VER="fpc-3.3.1" # note 1<br />
export SRC_DIR="$HOME/bin/FPC/SRC/$FPC_VER"<br />
export FPC_DIR="$HOME/bin/FPC/$FPC_VER"<br />
mkdir -p "$FPC_DIR"<br />
mkdir -p "$SRC_DIR"<br />
<br />
cd "$SRC_DIR"<br />
unzip ~/Downloads/fpc.zip # note 2<br />
cd fpc<br />
make all # note 3<br />
<br />
make install INSTALL_PREFIX="$FPC_DIR"<br />
<br />
# note 4<br />
make crossinstall CPU_TARGET=arm OS_TARGET=embedded SUBARCH=armv6m CROSSBINDIR=/usr/bin BINUTILSPREFIX=arm-none-eabi- CROSSOPT="-CpARMV6M -CaEABI" INSTALL_PREFIX="$FPC_DIR"<br />
<br />
<br />
cp compiler/ppcx64 "$FPC_DIR"/bin/ppcx64 # note 5 <br />
cp compiler/ppcrossarm "$FPC_DIR"/bin/ppcrossarm # note 6<br />
<br />
# note 7<br />
cp rtl/embedded/arm/* "$FPC_DIR"/lib/fpc/3.3.1/units/arm-embedded/rtl/.<br />
<br />
cd "$FPC_DIR"<br />
mkdir -p etc<br />
bin/fpcmkcfg -d basepath="$FPC_DIR" -o etc/fpc.cfg # note 8<br />
ln -s "$FPC_DIR"/lib/fpc/3.3.1/units units # note 9<br />
<br />
<br />
cd bin<br />
echo "#!/bin/sh" > fpc.bash # note 10<br />
echo "# A script that ensure when we start fpc here, it ignores all other fpc.cfg" >> fpc.bash<br />
echo "$FPC_DIR""/bin/fpc -n @$FPC_DIR/etc/fpc.cfg \"\$@\"" >> fpc.bash<br />
chmod u+x fpc.bash<br />
<br />
* note 1 - right now (March 2024) fpc main thinks of it self as 3.3.1, this may change after the next release of FPC and its possible the necessary updates to make this all work will not be in the release.<br />
<br />
* note 2 - make sure you have that fpc.zip file there, main Dec 2024 or later.<br />
<br />
* note 3 - this takes a while.<br />
<br />
* note 4 - this takes a while too. And it places the RTL files in a different place than fpcupdeluxe.<br />
<br />
* note 5 - this is your host system compiler.<br />
<br />
* note 6 - and this is the cross compiler, we call fpc and it calls this one.<br />
<br />
* note 7 - these are some units that the RTL, built earlier, will need. Again, files end up in a different place than fpcupdeluxe uses. fpcupdeluxe uses ... arm-embedded/eabi/rtl/. We cannot support other Arm embedded systems here.<br />
<br />
* note 8 - this creates a pretty stock standard fpc.cfg file. <br />
<br />
* note 9 - we link the RTL files we made earlier to where the std config file expects them to be.<br />
<br />
* note 10 - this makes a script that calls fpc and passes parameters that ensure fpc takes no regard of any other fpc.cfg file, only the one we just made.<br />
<br />
===Setup a Lazarus project===<br />
<br />
Doing your Pico development in Lazarus does make things simpler once setup. You can use (some of) Lazarus's help and code tools systems, click compile (but obviously not run) and have all your errors nicely presented. You do not need to make any changes to Lazarus (apart from settings mentioned below), just about any recent Lazarus will do. But, as mentioned above, makes sense to dedicate an install to Pico because of an unusual setting, next papa. <br />
<br />
Start a new Lazarus project in the usual way, to keep life simple, save it in a directory under 'projects' in Michael's directory tree. <br />
Firstly, a one off for this Lazarus instance, note this is NOT saved with the project. Open Options -> Files and look for the "Compiler executable" entry. Put in, with a full path, 'fpc.bash'. eg /home/YOUR-USER-NAME/bin/FPC/fpc-3.3.1/bin/fpc.bash (note fpcupdeluxe uses fpc.sh)<br />
<br />
Then, open the Project Options and set the following -<br />
<br />
Paths<br />
Other unit files (-Fu): ../pico-fpcexamples/units<br />
Include files (-Fi) : $(ProjOutDir)<br />
Libraries (-Fl);<br />
Unit output directory (-FU): lib/$(TargetCPU)-$(TargetOS)<br />
Target file name (-o) : bin/project1<br />
[x] Apply conventions<br />
<br />
Config and Target<br />
[x] Use standard compiler config...<br />
[ ] Use additional<br />
Target OS (-T) : Embedded<br />
Target CPU family (-P) : arm<br />
Target processor (-Cp) : ARMV6M<br />
<br />
Custom Options<br />
-Wpraspi_pico<br />
-godwarfsets<br />
-godwarfcpp<br />
-Xu<br />
-XParm-none-eabi-<br />
<br />
Note the last of the Custom Options, when you installed binutils-arm-none-eabi, you added a set of binutils for this particular processor. Some systems may call them arm-embedded-??? if yours uses that name scheme, change that Option accordingly.<br />
<br />
===Compile from Commandline===<br />
Its also reasonable to build from the command line, here is an example -<br />
<br />
#!/bin/bash<br />
echo "------- Using the fpc from 3.3.1 ---------"<br />
export MICHAELS_UNITS="$HOME"/Pico2/projects/pico-fpcexamples/units <br />
"$HOME"/bin/FPC/fpc-3.3.1/bin/fpc.bash -va -Xu -XParm-none-eabi- -Tembedded -Parm -CpARMV6M -Fu"$MICHAELS_UNITS" -Wpraspi_pico project1<br />
<br />
Note: -XParm-none-eabi- indicates how the binutils are named. If yours are named differently, change it !<br />
<br />
===See also===<br />
<br />
* [[ARM Embedded Tutorial - FPC and the Raspberry Pi Pico|ARM Embedded Raspberry Pi Pico Tutorials]]<br />
<br />
<br />
[[Category:Operating Systems and Platforms]]<br />
[[Category:ARM]]<br />
[[Category:Microcontrollers]]<br />
[[Category:Embedded]]<br />
[[Category:Tutorials]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Building_a_Pico_Compiler&diff=158387Building a Pico Compiler2024-03-09T09:09:07Z<p>Dbannon: clean up</p>
<hr />
<div>=Building a Pico Compiler=<br />
<br />
One of FPC's great features is its ability to provide cross compilers. A cross compiler allows you to run on one system and make a binary for another, possibly completely different system. When making binaries for embedded targets, its the only way to go !<br />
<br />
=== fpcupdeluxe ===<br />
For most users, the most convenient way to get a cross compiler for the Raspberry Pi Pico is to use [https://github.com/LongDirtyAnimAlf/fpcupdeluxe fpcupdeluxe] to do it all. It will install just FPC or FPC and Lazarus for you. Note it installs the 'main' version of each, from time to time, one or the other may have bugs (thats the nature of development branches) but not often. Try again in a day or so if you have problems. <br />
<br />
But this page is really for those who want to build their own, or just understand the process. Maybe adapt it to another CPU. These instructions focus on using a 64bit Linux system as the host, Windows and perhaps Mac users can probably do something similar.<br />
<br />
{{Note| and this is important, this recipes produces a different file layout that fpupdeluxe. Its about simplicity not supporting a number of other Arm varieties for example. Do not mix this with a fpcupdeluxe or results may be unpredictable.}}<br />
<br />
===The constraints===<br />
* If you are a Lazarus user, it makes sense to have a stand alone Lazarus install for your Pico work, you could switch a single install (by changing Tools->Options->Files->Compiler_executable) but it could get annoying. Multiple installs means installing Lazarus from source, remarkably easy.<br />
* If you already have a Source install of FPC main December 2023 or later, you can use that, with some care. Again, might be easier and safer to have a dedicated version. You do need an existing install of FPC322, if yours is installed in read only space (installed from a deb or rpm) then I suggest you work here with with a fresh copy of FPC and "install" it into your own space. <br />
<br />
===Dependencies===<br />
* An existing, working, FPC322. <br />
* Install binutils-arm-none-eabi - that means ones for arm, no operating system (or embedded) and eabi indicates the arm subtype, no 'hf' means 'soft float'. <br />
* And, of course, you need Michael's git repo with pico-fpcexamples. These come from https://github.com/michael-ring/pico-fpcexamples Note that Michael's repo contain tow separate things, some nicely set example projects and, in 'units' a number of libraries (*.obj) and pascal units to link them into a pascal project.<br />
<br />
===The Process===<br />
Here are a series of command lines, I do not recommend you run them as a script (although I have) because you MUST check for errors at each step. I keep this in my home directory in a structure like this, change it of course but change the commands if you do ! The commands here will create two fpc-3.3.1 directories, if you have them already, ensure they are empty or change the commands. Note that I have already downloaded fpc.zip to my Downloads dir.<br />
<br />
===Assumed Dir Structure===<br />
$HOME/bin/FPC<br />
fpc-3.2.2 .....<br />
fpc-3.3.1<br />
SRC<br />
fpc-3.2.2 ....<br />
fpc-3.3.1<br />
<br />
=====The commands=====<br />
<br />
#!/bin/bash<br />
<br />
export FPC_VER="fpc-3.3.1" # note 1<br />
export SRC_DIR="$HOME/bin/FPC/SRC/$FPC_VER"<br />
export FPC_DIR="$HOME/bin/FPC/$FPC_VER"<br />
mkdir -p "$FPC_DIR"<br />
mkdir -p "$SRC_DIR"<br />
<br />
cd "$SRC_DIR"<br />
unzip ~/Downloads/fpc.zip # note 2<br />
cd fpc<br />
make all # note 3<br />
<br />
make install INSTALL_PREFIX="$FPC_DIR"<br />
<br />
# note 4<br />
make crossinstall CPU_TARGET=arm OS_TARGET=embedded SUBARCH=armv6m CROSSBINDIR=/usr/bin BINUTILSPREFIX=arm-none-eabi- CROSSOPT="-CpARMV6M -CaEABI" INSTALL_PREFIX="$FPC_DIR"<br />
<br />
<br />
cp compiler/ppcx64 "$FPC_DIR"/bin/ppcx64 # note 5 <br />
cp compiler/ppcrossarm "$FPC_DIR"/bin/ppcrossarm # note 6<br />
<br />
# note 7<br />
cp rtl/embedded/arm/* "$FPC_DIR"/lib/fpc/3.3.1/units/arm-embedded/rtl/.<br />
<br />
cd "$FPC_DIR"<br />
mkdir -p etc<br />
bin/fpcmkcfg -d basepath="$FPC_DIR" -o etc/fpc.cfg # note 8<br />
ln -s "$FPC_DIR"/lib/fpc/3.3.1/units units # note 9<br />
<br />
<br />
cd bin<br />
echo "#!/bin/sh" > fpc.bash # note 10<br />
echo "# A script that ensure when we start fpc here, it ignores all other fpc.cfg" >> fpc.bash<br />
echo "$FPC_DIR""/bin/fpc -n @$FPC_DIR/etc/fpc.cfg \"\$@\"" >> fpc.bash<br />
chmod u+x fpc.bash<br />
<br />
* note 1 - right now (March 2024) fpc main thinks of it self as 3.3.1, this may change after the next release of FPC and its possible the necessary updates to make this all work will not be in the release.<br />
<br />
* note 2 - make sure you have that fpc.zip file there, main Dec 2024 or later.<br />
<br />
* note 3 - this takes a while.<br />
<br />
* note 4 - this takes a while too. And it places the RTL files in a different place than fpcupdeluxe.<br />
<br />
* note 5 - this is your host system compiler.<br />
<br />
* note 6 - and this is the cross compiler, we call fpc and it calls this one.<br />
<br />
* note 7 - these are some units that the RTL, built earlier, will need. Again, files end up in a different place than fpcupdeluxe uses. fpcupdeluxe uses ... arm-embedded/eabi/rtl/. We cannot support other Arm embedded systems here.<br />
<br />
* note 8 - this creates a pretty stock standard fpc.cfg file. <br />
<br />
* note 9 - we link the RTL files we made earlier to where the std config file expects them to be.<br />
<br />
* note 10 - this makes a script that calls fpc and passes parameters that ensure fpc takes no regard of any other fpc.cfg file, only the one we just made.<br />
<br />
===Setup a Lazarus project===<br />
<br />
Doing your Pico development in Lazarus does make things simpler once setup. You can use (some of) Lazarus's help and code tools systems, click compile (but obviously not run) and have all your errors nicely presented. You do not need to make any changes to Lazarus (apart from settings mentioned below), just about any recent Lazarus will do. But, as mentioned above, makes sense to dedicate an install to Pico because of an unusual setting, next papa. <br />
<br />
Start a new Lazarus project in the usual way, to keep life simple, save it in a directory under 'projects' in Michael's directory tree. <br />
Firstly, a one off for this Lazarus instance, note this is NOT saved with the project. Open Options -> Files and look for the "Compiler executable" entry. Put in, with a full path, 'fpc.bash'. eg /home/YOUR-USER-NAME/bin/FPC/fpc-3.3.1/bin/fpc.bash (note fpcupdeluxe uses fpc.sh)<br />
<br />
Then, open the Project Options and set the following -<br />
<br />
Paths<br />
Other unit files (-Fu): ../pico-fpcexamples/units<br />
Include files (-Fi) : $(ProjOutDir)<br />
Libraries (-Fl);<br />
Unit output directory (-FU): lib/$(TargetCPU)-$(TargetOS)<br />
Target file name (-o) : bin/project1<br />
[x] Apply conventions<br />
<br />
Config and Target<br />
[x] Use standard compiler config...<br />
[ ] Use additional<br />
Target OS (-T) : Embedded<br />
Target CPU family (-P) : arm<br />
Target processor (-Cp) : ARMV6M<br />
<br />
Custom Options<br />
-Wpraspi_pico<br />
-godwarfsets<br />
-godwarfcpp<br />
-Xu<br />
-XParm-none-eabi-<br />
<br />
Note the last of the Custom Options, when you installed binutils-arm-none-eabi, you added a set of binutils for this particular processor. Some systems may call them arm-embedded-??? if yours uses that name scheme, change that Option accordingly.<br />
<br />
===Compile from Commandline===<br />
Its also reasonable to build from the command line, here is an example -<br />
<br />
#!/bin/bash<br />
echo "------- Using the fpc from 3.3.1 ---------"<br />
export MICHAELS_UNITS="$HOME"/Pico2/projects/pico-fpcexamples/units <br />
"$HOME"/bin/FPC/fpc-3.3.1/bin/fpc.bash -va -Xu -XParm-none-eabi- -Tembedded -Parm -CpARMV6M -Fu"$MICHAELS_UNITS" -Wpraspi_pico project1<br />
<br />
Note: -XParm-none-eabi- indicates how the binutils are named. If yours are named differently, change it !<br />
<br />
===See also===<br />
<br />
* [[ARM Embedded Tutorial - FPC and the Raspberry Pi Pico|ARM Embedded Raspberry Pi Pico Tutorials]]<br />
<br />
<br />
[[Category:Operating Systems and Platforms]]<br />
[[Category:ARM]]<br />
[[Category:Microcontrollers]]<br />
[[Category:Embedded]]<br />
[[Category:Tutorials]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Building_a_Pico_Compiler&diff=158386Building a Pico Compiler2024-03-09T09:01:06Z<p>Dbannon: typo</p>
<hr />
<div>=Building a Pico Compiler=<br />
<br />
One of FPC's great features is its ability to provide cross compilers. A cross compiler allows you to run on one system and make a binary for another, possibly completely different system. When making binaries for embedded targets, its the only way to go !<br />
<br />
=== fpcupdeluxe ===<br />
For most users, the most convenient way to get a cross compiler for the Raspberry Pi Pico is to use **fpcupdeluxe**. Download it from https://github.com/LongDirtyAnimAlf/fpcupdeluxe . It will install just FPC or FPC and Lazarus for you. Note it installs the 'main' version of each, from time to time, one or the other may have bugs (thats the nature of development branches) but not often. Try again in a day or so if you have problems. <br />
<br />
But this page is really for those who want to build their own, or just understand the process. Maybe adapt it to another CPU. These instructions focus on using a 64bit Linux system as the host, Windows and perhaps Mac users can probably do something similar.<br />
<br />
{{Note| and this is important, this recipes produces a different file layout that fpupdeluxe. Its about simplicity not supporting a number of other Arm varieties for example. Do not mix this with a fpcupdeluxe or results may be unpredictable.}}<br />
<br />
===The constraints===<br />
* If you are a Lazarus user, it makes sense to have a stand alone Lazarus install for your Pico work, you could switch a single install (by changing Tools->Options->Files->Compiler_executable) but it could get annoying. Multiple installs means installing Lazarus from source, remarkably easy.<br />
* If you already have a Source install of FPC main December 2023 or later, you can use that, with some care. Again, might be easier and safer to have a dedicated version. You do need an existing install of FPC322, if yours is installed in read only space (installed from a deb or rpm) then I suggest you work here with with a fresh copy of FPC and "install" it into your own space. <br />
<br />
===Dependencies===<br />
* An existing, working, FPC322. <br />
* Install binutils-arm-none-eabi - that means ones for arm, no operating system (or embedded) and eabi indicates the arm subtype, no 'hf' means 'soft float'. <br />
* And, of course, you need Michael's git repo with pico-fpcexamples. These come from https://github.com/michael-ring/pico-fpcexamples Note that Michael's repo contain tow separate things, some nicely set example projects and, in 'units' a number of libraries (*.obj) and pascal units to link them into a pascal project.<br />
<br />
===The Process===<br />
Here are a series of command lines, I do not recommend you run them as a script (although I have) because you MUST check for errors at each step. I keep this in my home directory in a structure like this, change it of course but change the commands if you do ! The commands here will create two fpc-3.3.1 directories, if you have them already, ensure they are empty or change the commands. Note that I have already downloaded fpc.zip to my Downloads dir.<br />
<br />
===Assumed Dir Structure===<br />
$HOME/bin/FPC<br />
fpc-3.2.2 .....<br />
fpc-3.3.1<br />
SRC<br />
fpc-3.2.2 ....<br />
fpc-3.3.1<br />
<br />
=====The commands=====<br />
<br />
#!/bin/bash<br />
<br />
export FPC_VER="fpc-3.3.1" # note 1<br />
export SRC_DIR="$HOME/bin/FPC/SRC/$FPC_VER"<br />
export FPC_DIR="$HOME/bin/FPC/$FPC_VER"<br />
mkdir -p "$FPC_DIR"<br />
mkdir -p "$SRC_DIR"<br />
<br />
cd "$SRC_DIR"<br />
unzip ~/Downloads/fpc.zip # note 2<br />
cd fpc<br />
make all # note 3<br />
<br />
make install INSTALL_PREFIX="$FPC_DIR"<br />
<br />
# note 4<br />
make crossinstall CPU_TARGET=arm OS_TARGET=embedded SUBARCH=armv6m CROSSBINDIR=/usr/bin BINUTILSPREFIX=arm-none-eabi- CROSSOPT="-CpARMV6M -CaEABI" INSTALL_PREFIX="$FPC_DIR"<br />
<br />
<br />
cp compiler/ppcx64 "$FPC_DIR"/bin/ppcx64 # note 5 <br />
cp compiler/ppcrossarm "$FPC_DIR"/bin/ppcrossarm # note 6<br />
<br />
# note 7<br />
cp rtl/embedded/arm/* "$FPC_DIR"/lib/fpc/3.3.1/units/arm-embedded/rtl/.<br />
<br />
cd "$FPC_DIR"<br />
mkdir -p etc<br />
bin/fpcmkcfg -d basepath="$FPC_DIR" -o etc/fpc.cfg # note 8<br />
ln -s "$FPC_DIR"/lib/fpc/3.3.1/units units # note 9<br />
<br />
<br />
cd bin<br />
echo "#!/bin/sh" > fpc.bash # note 10<br />
echo "# A script that ensure when we start fpc here, it ignores all other fpc.cfg" >> fpc.bash<br />
echo "$FPC_DIR""/bin/fpc -n @$FPC_DIR/etc/fpc.cfg \"\$@\"" >> fpc.bash<br />
chmod u+x fpc.bash<br />
<br />
* note 1 - right now (March 2024) fpc main thinks of it self as 3.3.1, this may change after the next release of FPC and its possible the necessary updates to make this all work will not be in the release.<br />
<br />
* note 2 - make sure you have that fpc.zip file there, main Dec 2024 or later.<br />
<br />
* note 3 - this takes a while.<br />
<br />
* note 4 - this takes a while too. And it places the RTL files in a different place than fpcupdeluxe.<br />
<br />
* note 5 - this is your host system compiler.<br />
<br />
* note 6 - and this is the cross compiler, we call fpc and it calls this one.<br />
<br />
* note 7 - these are some units that the RTL, built earlier, will need. Again, files end up in a different place than fpcupdeluxe uses. fpcupdeluxe uses ... arm-embedded/eabi/rtl/. We cannot support other Arm embedded systems here.<br />
<br />
* note 8 - this creates a pretty stock standard fpc.cfg file. <br />
<br />
* note 9 - we link the RTL files we made earlier to where the std config file expects them to be.<br />
<br />
* note 10 - this makes a script that calls fpc and passes parameters that ensure fpc takes no regard of any other fpc.cfg file, only the one we just made.<br />
<br />
===Setup a Lazarus project===<br />
<br />
Doing your Pico development in Lazarus does make things simpler once setup. You can use (some of) Lazarus's help and code tools systems, click compile (but obviously not run) and have all your errors nicely presented. You do not need to make any changes to Lazarus (apart from settings mentioned below), just about any recent Lazarus will do. But, as mentioned above, makes sense to dedicate an install to Pico because of an unusual setting, next papa. <br />
<br />
Start a new Lazarus project in the usual way, to keep life simple, save it in a directory under 'projects' in Michael's directory tree. <br />
Firstly, a one off for this Lazarus instance, note this is NOT saved with the project. Open Options -> Files and look for the "Compiler executable" entry. Put in, with a full path, 'fpc.bash'. eg /home/YOUR-USER-NAME/bin/FPC/fpc-3.3.1/bin/fpc.bash (note fpcupdeluxe uses fpc.sh)<br />
<br />
Then, open the Project Options and set the following -<br />
<br />
Paths<br />
Other unit files (-Fu): ../pico-fpcexamples/units<br />
Include files (-Fi) : $(ProjOutDir)<br />
Libraries (-Fl);<br />
Unit output directory (-FU): lib/$(TargetCPU)-$(TargetOS)<br />
Target file name (-o) : bin/project1<br />
[x] Apply conventions<br />
<br />
Config and Target<br />
[x] Use standard compiler config...<br />
[ ] Use additional<br />
Target OS (-T) : Embedded<br />
Target CPU family (-P) : arm<br />
Target processor (-Cp) : ARMV6M<br />
<br />
Custom Options<br />
-Wpraspi_pico<br />
-godwarfsets<br />
-godwarfcpp<br />
-Xu<br />
-XParm-none-eabi-<br />
<br />
Note the last of the Custom Options, when you installed binutils-arm-none-eabi, you added a set of binutils for this particular processor. Some systems may call them arm-embedded-??? if yours uses that name scheme, change that Option accordingly.<br />
<br />
===Compile from Commandline===<br />
Its also reasonable to build from the command line, here is an example -<br />
<br />
#!/bin/bash<br />
echo "------- Using the fpc from 3.3.1 ---------"<br />
export MICHAELS_UNITS="$HOME"/Pico2/projects/pico-fpcexamples/units <br />
"$HOME"/bin/FPC/fpc-3.3.1/bin/fpc.bash -va -Xu -XParm-none-eabi- -Tembedded -Parm -CpARMV6M -Fu"$MICHAELS_UNITS" -Wpraspi_pico project1<br />
<br />
Note: -XParm-none-eabi- indicates how the binutils are named. If yours are named differently, change it !<br />
<br />
===See also===<br />
<br />
* [[ARM Embedded Tutorial - FPC and the Raspberry Pi Pico|ARM Embedded Raspberry Pi Pico Tutorials]]<br />
<br />
<br />
[[Category:Operating Systems and Platforms]]<br />
[[Category:ARM]]<br />
[[Category:Microcontrollers]]<br />
[[Category:Embedded]]<br />
[[Category:Tutorials]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Building_a_Pico_Compiler&diff=158385Building a Pico Compiler2024-03-09T08:58:49Z<p>Dbannon: updates re limitations of file layout used.</p>
<hr />
<div>=Building a Pico Compiler=<br />
<br />
One of FPC's great features is its ability to provide cross compilers. A cross compiler allows you to run on one system and make a binary for another, possibly completely different system. When making binaries for embedded targets, its the only way to go !<br />
<br />
=== fpcupdeluxe ===<br />
For most users, the most convenient way to get a cross compiler for the Raspberry Pi Pico is to use **fpcupdeluxe**. Download it from https://github.com/LongDirtyAnimAlf/fpcupdeluxe . It will install just FPC or FPC and Lazarus for you. Note it installs the 'main' version of each, from time to time, one or the other may have bugs (thats the nature of development branches) but not often. Try again in a day or so if you have problems. <br />
<br />
But this page is really for those who want to build their own, or just understand the process. Maybe adapt it to another CPU. These instructions focus on using a 64bit Linux system as the host, Windows and perhaps Mac users can probably do something similar.<br />
<br />
{{Note| and this is important, this recipte produces a different file layout that fpupdeluxe. Its about simplicity not supporting a number of other Arm varieties for example. Do not mix this with a fpcupdeluxe or results may be unpredictable.}}<br />
<br />
===The constraints===<br />
* If you are a Lazarus user, it makes sense to have a stand alone Lazarus install for your Pico work, you could switch a single install (by changing Tools->Options->Files->Compiler_executable) but it could get annoying. Multiple installs means installing Lazarus from source, remarkably easy.<br />
* If you already have a Source install of FPC main December 2023 or later, you can use that, with some care. Again, might be easier and safer to have a dedicated version. You do need an existing install of FPC322, if yours is installed in read only space (installed from a deb or rpm) then I suggest you work here with with a fresh copy of FPC and "install" it into your own space. <br />
<br />
===Dependencies===<br />
* An existing, working, FPC322. <br />
* Install binutils-arm-none-eabi - that means ones for arm, no operating system (or embedded) and eabi indicates the arm subtype, no 'hf' means 'soft float'. <br />
* And, of course, you need Michael's git repo with pico-fpcexamples. These come from https://github.com/michael-ring/pico-fpcexamples Note that Michael's repo contain tow separate things, some nicely set example projects and, in 'units' a number of libraries (*.obj) and pascal units to link them into a pascal project.<br />
<br />
===The Process===<br />
Here are a series of command lines, I do not recommend you run them as a script (although I have) because you MUST check for errors at each step. I keep this in my home directory in a structure like this, change it of course but change the commands if you do ! The commands here will create two fpc-3.3.1 directories, if you have them already, ensure they are empty or change the commands. Note that I have already downloaded fpc.zip to my Downloads dir.<br />
<br />
===Assumed Dir Structure===<br />
$HOME/bin/FPC<br />
fpc-3.2.2 .....<br />
fpc-3.3.1<br />
SRC<br />
fpc-3.2.2 ....<br />
fpc-3.3.1<br />
<br />
=====The commands=====<br />
<br />
#!/bin/bash<br />
<br />
export FPC_VER="fpc-3.3.1" # note 1<br />
export SRC_DIR="$HOME/bin/FPC/SRC/$FPC_VER"<br />
export FPC_DIR="$HOME/bin/FPC/$FPC_VER"<br />
mkdir -p "$FPC_DIR"<br />
mkdir -p "$SRC_DIR"<br />
<br />
cd "$SRC_DIR"<br />
unzip ~/Downloads/fpc.zip # note 2<br />
cd fpc<br />
make all # note 3<br />
<br />
make install INSTALL_PREFIX="$FPC_DIR"<br />
<br />
# note 4<br />
make crossinstall CPU_TARGET=arm OS_TARGET=embedded SUBARCH=armv6m CROSSBINDIR=/usr/bin BINUTILSPREFIX=arm-none-eabi- CROSSOPT="-CpARMV6M -CaEABI" INSTALL_PREFIX="$FPC_DIR"<br />
<br />
<br />
cp compiler/ppcx64 "$FPC_DIR"/bin/ppcx64 # note 5 <br />
cp compiler/ppcrossarm "$FPC_DIR"/bin/ppcrossarm # note 6<br />
<br />
# note 7<br />
cp rtl/embedded/arm/* "$FPC_DIR"/lib/fpc/3.3.1/units/arm-embedded/rtl/.<br />
<br />
cd "$FPC_DIR"<br />
mkdir -p etc<br />
bin/fpcmkcfg -d basepath="$FPC_DIR" -o etc/fpc.cfg # note 8<br />
ln -s "$FPC_DIR"/lib/fpc/3.3.1/units units # note 9<br />
<br />
<br />
cd bin<br />
echo "#!/bin/sh" > fpc.bash # note 10<br />
echo "# A script that ensure when we start fpc here, it ignores all other fpc.cfg" >> fpc.bash<br />
echo "$FPC_DIR""/bin/fpc -n @$FPC_DIR/etc/fpc.cfg \"\$@\"" >> fpc.bash<br />
chmod u+x fpc.bash<br />
<br />
* note 1 - right now (March 2024) fpc main thinks of it self as 3.3.1, this may change after the next release of FPC and its possible the necessary updates to make this all work will not be in the release.<br />
<br />
* note 2 - make sure you have that fpc.zip file there, main Dec 2024 or later.<br />
<br />
* note 3 - this takes a while.<br />
<br />
* note 4 - this takes a while too. And it places the RTL files in a different place than fpcupdeluxe.<br />
<br />
* note 5 - this is your host system compiler.<br />
<br />
* note 6 - and this is the cross compiler, we call fpc and it calls this one.<br />
<br />
* note 7 - these are some units that the RTL, built earlier, will need. Again, files end up in a different place than fpcupdeluxe uses. fpcupdeluxe uses ... arm-embedded/eabi/rtl/. We cannot support other Arm embedded systems here.<br />
<br />
* note 8 - this creates a pretty stock standard fpc.cfg file. <br />
<br />
* note 9 - we link the RTL files we made earlier to where the std config file expects them to be.<br />
<br />
* note 10 - this makes a script that calls fpc and passes parameters that ensure fpc takes no regard of any other fpc.cfg file, only the one we just made.<br />
<br />
===Setup a Lazarus project===<br />
<br />
Doing your Pico development in Lazarus does make things simpler once setup. You can use (some of) Lazarus's help and code tools systems, click compile (but obviously not run) and have all your errors nicely presented. You do not need to make any changes to Lazarus (apart from settings mentioned below), just about any recent Lazarus will do. But, as mentioned above, makes sense to dedicate an install to Pico because of an unusual setting, next papa. <br />
<br />
Start a new Lazarus project in the usual way, to keep life simple, save it in a directory under 'projects' in Michael's directory tree. <br />
Firstly, a one off for this Lazarus instance, note this is NOT saved with the project. Open Options -> Files and look for the "Compiler executable" entry. Put in, with a full path, 'fpc.bash'. eg /home/YOUR-USER-NAME/bin/FPC/fpc-3.3.1/bin/fpc.bash (note fpcupdeluxe uses fpc.sh)<br />
<br />
Then, open the Project Options and set the following -<br />
<br />
Paths<br />
Other unit files (-Fu): ../pico-fpcexamples/units<br />
Include files (-Fi) : $(ProjOutDir)<br />
Libraries (-Fl);<br />
Unit output directory (-FU): lib/$(TargetCPU)-$(TargetOS)<br />
Target file name (-o) : bin/project1<br />
[x] Apply conventions<br />
<br />
Config and Target<br />
[x] Use standard compiler config...<br />
[ ] Use additional<br />
Target OS (-T) : Embedded<br />
Target CPU family (-P) : arm<br />
Target processor (-Cp) : ARMV6M<br />
<br />
Custom Options<br />
-Wpraspi_pico<br />
-godwarfsets<br />
-godwarfcpp<br />
-Xu<br />
-XParm-none-eabi-<br />
<br />
Note the last of the Custom Options, when you installed binutils-arm-none-eabi, you added a set of binutils for this particular processor. Some systems may call them arm-embedded-??? if yours uses that name scheme, change that Option accordingly.<br />
<br />
===Compile from Commandline===<br />
Its also reasonable to build from the command line, here is an example -<br />
<br />
#!/bin/bash<br />
echo "------- Using the fpc from 3.3.1 ---------"<br />
export MICHAELS_UNITS="$HOME"/Pico2/projects/pico-fpcexamples/units <br />
"$HOME"/bin/FPC/fpc-3.3.1/bin/fpc.bash -va -Xu -XParm-none-eabi- -Tembedded -Parm -CpARMV6M -Fu"$MICHAELS_UNITS" -Wpraspi_pico project1<br />
<br />
Note: -XParm-none-eabi- indicates how the binutils are named. If yours are named differently, change it !<br />
<br />
===See also===<br />
<br />
* [[ARM Embedded Tutorial - FPC and the Raspberry Pi Pico|ARM Embedded Raspberry Pi Pico Tutorials]]<br />
<br />
<br />
[[Category:Operating Systems and Platforms]]<br />
[[Category:ARM]]<br />
[[Category:Microcontrollers]]<br />
[[Category:Embedded]]<br />
[[Category:Tutorials]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Building_a_Pico_Compiler&diff=158383Building a Pico Compiler2024-03-08T23:30:33Z<p>Dbannon: a touch of formatting</p>
<hr />
<div>=Building a Pico Compiler=<br />
<br />
One of FPC's great features is its ability to provide cross compilers. A cross compiler allows you to run on one system and make a binary for another, possibly completely different system. When making binaries for embedded targets, its the only way to go !<br />
<br />
For most users, the most convenient way to get a cross compiler for the Raspberry Pi Pico is to use **fpcupdeluxe** but for those who want to build their own, or just understand the process, here is an example. These instructions focus on using a 64bit Linux system as the host, Windows and perhaps Mac users can probably do something similar.<br />
<br />
===The constraints===<br />
* If you are a Lazarus user, it makes sense to have a stand alone Lazarus install for your Pico work, you could switch a single install (by changing Tools->Options->Files->Compiler_executable) but it could get annoying. Multiple installs means installing Lazarus from source, remarkably easy.<br />
* If you already have a Source install of FPC main December 2023 or later, you can use that, with some care. Again, might be easier and safer to have a dedicated version. You do need an existing install of FPC322, if yours is installed in read only space (installed from a deb or rpm) then I suggest you work here with with a fresh copy of FPC and "install" it into your own space. <br />
<br />
===Dependencies===<br />
* An existing, working, FPC322. <br />
* Install binutils-arm-none-eabi - that means ones for arm, no operating system (or embedded) and eabi indicates the arm subtype, no 'hf' means 'soft float'. <br />
* And, of course, you need Michael's git repo with pico-fpcexamples. These come from https://github.com/michael-ring/pico-fpcexamples Note that Michael's repo contain tow separate things, some nicely set example projects and, in 'units' a number of libraries (*.obj) and pascal units to link them into a pascal project.<br />
<br />
===The Process===<br />
Here are a series of command lines, I do not recommend you run them as a script (although I have) because you MUST check for errors at each step. I keep this in my home directory in a structure like this, change it of course but change the commands if you do ! The commands here will create two fpc-3.3.1 directories, if you have them already, ensure they are empty or change the commands. Note that I have already downloaded fpc.zip to my Downloads dir.<br />
<br />
===Assumed Dir Structure===<br />
$HOME/bin/FPC<br />
fpc-3.2.2 .....<br />
fpc-3.3.1<br />
SRC<br />
fpc-3.2.2 ....<br />
fpc-3.3.1<br />
<br />
=====The commands=====<br />
<br />
#!/bin/bash<br />
<br />
export FPC_VER="fpc-3.3.1" # note 1<br />
export SRC_DIR="$HOME/bin/FPC/SRC/$FPC_VER"<br />
export FPC_DIR="$HOME/bin/FPC/$FPC_VER"<br />
mkdir -p "$FPC_DIR"<br />
mkdir -p "$SRC_DIR"<br />
<br />
cd "$SRC_DIR"<br />
unzip ~/Downloads/fpc.zip # note 2<br />
cd fpc<br />
make all # note 3<br />
<br />
make install INSTALL_PREFIX="$FPC_DIR"<br />
<br />
# note 4<br />
make crossinstall CPU_TARGET=arm OS_TARGET=embedded SUBARCH=armv6m CROSSBINDIR=/usr/bin BINUTILSPREFIX=arm-none-eabi- CROSSOPT="-CpARMV6M -CaEABI" INSTALL_PREFIX="$FPC_DIR"<br />
<br />
<br />
cp compiler/ppcx64 "$FPC_DIR"/bin/ppcx64 # note 5 <br />
cp compiler/ppcrossarm "$FPC_DIR"/bin/ppcrossarm # note 6<br />
<br />
# note 7<br />
cp rtl/embedded/arm/* "$FPC_DIR"/lib/fpc/3.3.1/units/arm-embedded/rtl/.<br />
<br />
cd "$FPC_DIR"<br />
mkdir -p etc<br />
bin/fpcmkcfg -d basepath="$FPC_DIR" -o etc/fpc.cfg # note 8<br />
ln -s "$FPC_DIR"/lib/fpc/3.3.1/units units # note 9<br />
<br />
<br />
cd bin<br />
echo "#!/bin/sh" > fpc.bash # note 10<br />
echo "# A script that ensure when we start fpc here, it ignores all other fpc.cfg" >> fpc.bash<br />
echo "$FPC_DIR""/bin/fpc -n @$FPC_DIR/etc/fpc.cfg \"\$@\"" >> fpc.bash<br />
chmod u+x fpc.bash<br />
<br />
* note 1 - right now (March 2024) fpc main thinks of it self as 3.3.1, this may change after the next release of FPC and its possible the necessary updates to make this all work will not be in the release.<br />
<br />
* note 2 - make sure you have that fpc.zip file there, main Dec 2024 or later.<br />
<br />
* note 3 - this takes a while.<br />
<br />
* note 4 - this takes a while too.<br />
<br />
* note 5 - this is your host system compiler.<br />
<br />
* note 6 - and this is the cross compiler, we call fpc and it calls this one.<br />
<br />
* note 7 - these are some units that the RTL, built earlier, will need.<br />
<br />
* note 8 - this creates a pretty stock standard fpc.cfg file. <br />
<br />
* note 9 - we link the RTL files we made earlier to where the std config file expects them to be.<br />
<br />
* note 10 - this makes a script that calls fpc and passes parameters that ensure fpc takes no regard of any other fpc.cfg file, only the one we just made.<br />
<br />
===Setup a Lazarus project===<br />
<br />
Doing your Pico development in Lazarus does make things simpler once setup. You can use (some of) Lazarus's help and code tools systems, click compile (but obviously not run) and have all your errors nicely presented. You do not need to make any changes to Lazarus (apart from settings mentioned below), just about any recent Lazarus will do. But, as mentioned above, makes sense to dedicate an install to Pico because of an unusual setting, next papa. <br />
<br />
Start a new Lazarus project in the usual way, to keep life simple, save it in a directory under 'projects' in Michael's directory tree. <br />
Firstly, a one off for this Lazarus instance, note this is NOT saved with the project. Open Options -> Files and look for the "Compiler executable" entry. Put in, with a full path, 'fpc.bash'. eg /home/YOUR-USER-NAME/bin/FPC/fpc-3.3.1/bin/fpc.bash (note fpcupdeluxe uses fpc.sh)<br />
<br />
Then, open the Project Options and set the following -<br />
<br />
Paths<br />
Other unit files (-Fu): ../pico-fpcexamples/units<br />
Include files (-Fi) : $(ProjOutDir)<br />
Libraries (-Fl);<br />
Unit output directory (-FU): lib/$(TargetCPU)-$(TargetOS)<br />
Target file name (-o) : bin/project1<br />
[x] Apply conventions<br />
<br />
Config and Target<br />
[x] Use standard compiler config...<br />
[ ] Use additional<br />
Target OS (-T) : Embedded<br />
Target CPU family (-P) : arm<br />
Target processor (-Cp) : ARMV6M<br />
<br />
Custom Options<br />
-Wpraspi_pico<br />
-godwarfsets<br />
-godwarfcpp<br />
-Xu<br />
-XParm-none-eabi-<br />
<br />
Note the last of the Custom Options, when you installed binutils-arm-none-eabi, you added a set of binutils for this particular processor. Some systems may call them arm-embedded-??? if yours uses that name scheme, change that Option accordingly.<br />
<br />
===Compile from Commandline===<br />
Its also reasonable to build from the command line, here is an example -<br />
<br />
#!/bin/bash<br />
echo "------- Using the fpc from 3.3.1 ---------"<br />
export MICHAELS_UNITS="$HOME"/Pico2/projects/pico-fpcexamples/units <br />
"$HOME"/bin/FPC/fpc-3.3.1/bin/fpc.bash -va -Xu -XParm-none-eabi- -Tembedded -Parm -CpARMV6M -Fu"$MICHAELS_UNITS" -Wpraspi_pico project1<br />
<br />
Note: -XParm-none-eabi- indicates how the binutils are named. If yours are named differently, change it !<br />
<br />
===See also===<br />
<br />
* [[ARM Embedded Tutorial - FPC and the Raspberry Pi Pico|ARM Embedded Raspberry Pi Pico Tutorials]]<br />
<br />
<br />
[[Category:Operating Systems and Platforms]]<br />
[[Category:ARM]]<br />
[[Category:Microcontrollers]]<br />
[[Category:Embedded]]<br />
[[Category:Tutorials]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Building_a_Pico_Compiler&diff=158377Building a Pico Compiler2024-03-08T10:11:25Z<p>Dbannon: /* Compile from Commandline */</p>
<hr />
<div>=Building a Pico Compiler=<br />
<br />
One of FPC's great features is its ability to provide cross compilers. A cross compiler allows you to run on one system and make a binary for another, possibly completely different system. When making binaries for embedded targets, its the only way to go !<br />
<br />
For most users, the most convenient way to get a cross compiler for the Raspberry Pi Pico is to use **fpcupdeluxe** but for those who want to build their own, or just understand the process, here is an example. These instructions focus on using a 64bit Linux system as the host, Windows and perhaps Mac users can probably do something similar.<br />
<br />
===The constraints===<br />
* If you are a Lazarus user, it makes sense to have a stand alone Lazarus install for your Pico work, you could switch a single install (by changing Tools->Options->Files->Compiler_executable) but it could get annoying. Multiple installs means installing Lazarus from source, remarkably easy.<br />
* If you already have a Source install of FPC main December 2023 or later, you can use that, with some care. Again, might be easier and safer to have a dedicated version. You do need an existing install of FPC322, if yours is installed in read only space (installed from a deb or rpm) then I suggest you work here with with a fresh copy of FPC and "install" it into your own space. <br />
<br />
===Dependencies===<br />
* An existing, working, FPC322. <br />
* Install binutils-arm-none-eabi - that means ones for arm, no operating system (or embedded) and eabi indicates the arm subtype, no 'hf' means 'soft float'. <br />
* And, of course, you need Michael's git repo with pico-fpcexamples. These come from https://github.com/michael-ring/pico-fpcexamples Note that Michael's repo contain tow separate things, some nicely set example projects and, in 'units' a number of libraries (*.obj) and pascal units to link them into a pascal project.<br />
<br />
===The Process===<br />
Here are a series of command lines, I do not recommend you run them as a script (although I have) because you MUST check for errors at each step. I keep this in my home directory in a structure like this, change it of course but change the commands if you do ! The commands here will create two fpc-3.3.1 directories, if you have them already, ensure they are empty or change the commands. Note that I have already downloaded fpc.zip to my Downloads dir.<br />
<br />
===Assumed Dir Structure===<br />
$HOME/bin/FPC<br />
fpc-3.2.2 .....<br />
fpc-3.3.1<br />
SRC<br />
fpc-3.2.2 ....<br />
fpc-3.3.1<br />
<br />
=====The commands=====<br />
<br />
#!/bin/bash<br />
<br />
export FPC_VER="fpc-3.3.1" # note 1<br />
export SRC_DIR="$HOME/bin/FPC/SRC/$FPC_VER"<br />
export FPC_DIR="$HOME/bin/FPC/$FPC_VER"<br />
mkdir -p "$FPC_DIR"<br />
mkdir -p "$SRC_DIR"<br />
<br />
cd "$SRC_DIR"<br />
unzip ~/Downloads/fpc.zip # note 2<br />
cd fpc<br />
make all # note 3<br />
<br />
make install INSTALL_PREFIX="$FPC_DIR"<br />
<br />
# note 4<br />
make crossinstall CPU_TARGET=arm OS_TARGET=embedded SUBARCH=armv6m CROSSBINDIR=/usr/bin BINUTILSPREFIX=arm-none-eabi- CROSSOPT="-CpARMV6M -CaEABI" INSTALL_PREFIX="$FPC_DIR"<br />
<br />
<br />
cp compiler/ppcx64 "$FPC_DIR"/bin/ppcx64 # note 5 <br />
cp compiler/ppcrossarm "$FPC_DIR"/bin/ppcrossarm # note 6<br />
<br />
# note 7<br />
cp rtl/embedded/arm/* "$FPC_DIR"/lib/fpc/3.3.1/units/arm-embedded/rtl/.<br />
<br />
cd "$FPC_DIR"<br />
mkdir -p etc<br />
bin/fpcmkcfg -d basepath="$FPC_DIR" -o etc/fpc.cfg # note 8<br />
ln -s "$FPC_DIR"/lib/fpc/3.3.1/units units # note 9<br />
<br />
<br />
cd bin<br />
echo "#!/bin/sh" > fpc.bash # note 10<br />
echo "# A script that ensure when we start fpc here, it ignores all other fpc.cfg" >> fpc.bash<br />
echo "$FPC_DIR""/bin/fpc -n @$FPC_DIR/etc/fpc.cfg \"\$@\"" >> fpc.bash<br />
chmod u+x fpc.bash<br />
<br />
note 1 - right now (March 2024) fpc main thinks of it self as 3.3.1, this may change after the next release of FPC and its possible the necessary updates to make this all work will not be in the release.<br />
<br />
note 2 - make sure you have that fpc.zip file there, main Dec 2024 or later.<br />
<br />
note 3 - this takes a while.<br />
<br />
note 4 - this takes a while too.<br />
<br />
note 5 - this is your host system compiler.<br />
<br />
note 6 - and this is the cross compiler, we call fpc and it calls this one.<br />
<br />
note 7 - these are some units that the RTL, built earlier, will need.<br />
<br />
note 8 - this creates a pretty stock standard fpc.cfg file. <br />
<br />
note 9 - we link the RTL files we made earlier to where the std config file expects them to be.<br />
<br />
note 10 - this makes a script that calls fpc and passes parameters that ensure fpc takes no regard of any other fpc.cfg file, only the one we just made. <br />
<br />
===Setup a Lazarus project===<br />
<br />
Doing your Pico development in Lazarus does make things simpler once setup. You can use (some of) Lazarus's help and code tools systems, click compile (but obviously not run) and have all your errors nicely presented. You do not need to make any changes to Lazarus (apart from settings mentioned below), just about any recent Lazarus will do. But, as mentioned above, makes sense to dedicate an install to Pico because of an unusual setting, next papa. <br />
<br />
Start a new Lazarus project in the usual way, to keep life simple, save it in a directory under 'projects' in Michael's directory tree. <br />
Firstly, a one off for this Lazarus instance, note this is NOT saved with the project. Open Options -> Files and look for the "Compiler executable" entry. Put in, with a full path, 'fpc.bash'. eg /home/YOUR-USER-NAME/bin/FPC/fpc-3.3.1/bin/fpc.bash (note fpcupdeluxe uses fpc.sh)<br />
<br />
Then, open the Project Options and set the following -<br />
<br />
Paths<br />
Other unit files (-Fu): ../pico-fpcexamples/units<br />
Include files (-Fi) : $(ProjOutDir)<br />
Libraries (-Fl);<br />
Unit output directory (-FU): lib/$(TargetCPU)-$(TargetOS)<br />
Target file name (-o) : bin/project1<br />
[x] Apply conventions<br />
<br />
Config and Target<br />
[x] Use standard compiler config...<br />
[ ] Use additional<br />
Target OS (-T) : Embedded<br />
Target CPU family (-P) : arm<br />
Target processor (-Cp) : ARMV6M<br />
<br />
Custom Options<br />
-Wpraspi_pico<br />
-godwarfsets<br />
-godwarfcpp<br />
-Xu<br />
-XParm-none-eabi-<br />
<br />
Note the last of the Custom Options, when you installed binutils-arm-none-eabi, you added a set of binutils for this particular processor. Some systems may call them arm-embedded-??? if yours uses that name scheme, change that Option accordingly.<br />
<br />
===Compile from Commandline===<br />
Its also reasonable to build from the command line, here is an example -<br />
<br />
#!/bin/bash<br />
echo "------- Using the fpc from 3.3.1 ---------"<br />
export MICHAELS_UNITS="$HOME"/Pico2/projects/pico-fpcexamples/units <br />
"$HOME"/bin/FPC/fpc-3.3.1/bin/fpc.bash -va -Xu -XParm-none-eabi- -Tembedded -Parm -CpARMV6M -Fu"$MICHAELS_UNITS" -Wpraspi_pico project1<br />
<br />
Note: -XParm-none-eabi- indicates how the binutils are named. If yours are named differently, change it !<br />
<br />
===See also===<br />
<br />
* [[ARM Embedded Tutorial - FPC and the Raspberry Pi Pico|ARM Embedded Raspberry Pi Pico Tutorials]]<br />
<br />
<br />
[[Category:Operating Systems and Platforms]]<br />
[[Category:ARM]]<br />
[[Category:Microcontrollers]]<br />
[[Category:Embedded]]<br />
[[Category:Tutorials]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Building_a_Pico_Compiler&diff=158376Building a Pico Compiler2024-03-08T10:07:10Z<p>Dbannon: Compile from Commandline tweak</p>
<hr />
<div>=Building a Pico Compiler=<br />
<br />
One of FPC's great features is its ability to provide cross compilers. A cross compiler allows you to run on one system and make a binary for another, possibly completely different system. When making binaries for embedded targets, its the only way to go !<br />
<br />
For most users, the most convenient way to get a cross compiler for the Raspberry Pi Pico is to use **fpcupdeluxe** but for those who want to build their own, or just understand the process, here is an example. These instructions focus on using a 64bit Linux system as the host, Windows and perhaps Mac users can probably do something similar.<br />
<br />
===The constraints===<br />
* If you are a Lazarus user, it makes sense to have a stand alone Lazarus install for your Pico work, you could switch a single install (by changing Tools->Options->Files->Compiler_executable) but it could get annoying. Multiple installs means installing Lazarus from source, remarkably easy.<br />
* If you already have a Source install of FPC main December 2023 or later, you can use that, with some care. Again, might be easier and safer to have a dedicated version. You do need an existing install of FPC322, if yours is installed in read only space (installed from a deb or rpm) then I suggest you work here with with a fresh copy of FPC and "install" it into your own space. <br />
<br />
===Dependencies===<br />
* An existing, working, FPC322. <br />
* Install binutils-arm-none-eabi - that means ones for arm, no operating system (or embedded) and eabi indicates the arm subtype, no 'hf' means 'soft float'. <br />
* And, of course, you need Michael's git repo with pico-fpcexamples. These come from https://github.com/michael-ring/pico-fpcexamples Note that Michael's repo contain tow separate things, some nicely set example projects and, in 'units' a number of libraries (*.obj) and pascal units to link them into a pascal project.<br />
<br />
===The Process===<br />
Here are a series of command lines, I do not recommend you run them as a script (although I have) because you MUST check for errors at each step. I keep this in my home directory in a structure like this, change it of course but change the commands if you do ! The commands here will create two fpc-3.3.1 directories, if you have them already, ensure they are empty or change the commands. Note that I have already downloaded fpc.zip to my Downloads dir.<br />
<br />
===Assumed Dir Structure===<br />
$HOME/bin/FPC<br />
fpc-3.2.2 .....<br />
fpc-3.3.1<br />
SRC<br />
fpc-3.2.2 ....<br />
fpc-3.3.1<br />
<br />
=====The commands=====<br />
<br />
#!/bin/bash<br />
<br />
export FPC_VER="fpc-3.3.1" # note 1<br />
export SRC_DIR="$HOME/bin/FPC/SRC/$FPC_VER"<br />
export FPC_DIR="$HOME/bin/FPC/$FPC_VER"<br />
mkdir -p "$FPC_DIR"<br />
mkdir -p "$SRC_DIR"<br />
<br />
cd "$SRC_DIR"<br />
unzip ~/Downloads/fpc.zip # note 2<br />
cd fpc<br />
make all # note 3<br />
<br />
make install INSTALL_PREFIX="$FPC_DIR"<br />
<br />
# note 4<br />
make crossinstall CPU_TARGET=arm OS_TARGET=embedded SUBARCH=armv6m CROSSBINDIR=/usr/bin BINUTILSPREFIX=arm-none-eabi- CROSSOPT="-CpARMV6M -CaEABI" INSTALL_PREFIX="$FPC_DIR"<br />
<br />
<br />
cp compiler/ppcx64 "$FPC_DIR"/bin/ppcx64 # note 5 <br />
cp compiler/ppcrossarm "$FPC_DIR"/bin/ppcrossarm # note 6<br />
<br />
# note 7<br />
cp rtl/embedded/arm/* "$FPC_DIR"/lib/fpc/3.3.1/units/arm-embedded/rtl/.<br />
<br />
cd "$FPC_DIR"<br />
mkdir -p etc<br />
bin/fpcmkcfg -d basepath="$FPC_DIR" -o etc/fpc.cfg # note 8<br />
ln -s "$FPC_DIR"/lib/fpc/3.3.1/units units # note 9<br />
<br />
<br />
cd bin<br />
echo "#!/bin/sh" > fpc.bash # note 10<br />
echo "# A script that ensure when we start fpc here, it ignores all other fpc.cfg" >> fpc.bash<br />
echo "$FPC_DIR""/bin/fpc -n @$FPC_DIR/etc/fpc.cfg \"\$@\"" >> fpc.bash<br />
chmod u+x fpc.bash<br />
<br />
note 1 - right now (March 2024) fpc main thinks of it self as 3.3.1, this may change after the next release of FPC and its possible the necessary updates to make this all work will not be in the release.<br />
<br />
note 2 - make sure you have that fpc.zip file there, main Dec 2024 or later.<br />
<br />
note 3 - this takes a while.<br />
<br />
note 4 - this takes a while too.<br />
<br />
note 5 - this is your host system compiler.<br />
<br />
note 6 - and this is the cross compiler, we call fpc and it calls this one.<br />
<br />
note 7 - these are some units that the RTL, built earlier, will need.<br />
<br />
note 8 - this creates a pretty stock standard fpc.cfg file. <br />
<br />
note 9 - we link the RTL files we made earlier to where the std config file expects them to be.<br />
<br />
note 10 - this makes a script that calls fpc and passes parameters that ensure fpc takes no regard of any other fpc.cfg file, only the one we just made. <br />
<br />
===Setup a Lazarus project===<br />
<br />
Doing your Pico development in Lazarus does make things simpler once setup. You can use (some of) Lazarus's help and code tools systems, click compile (but obviously not run) and have all your errors nicely presented. You do not need to make any changes to Lazarus (apart from settings mentioned below), just about any recent Lazarus will do. But, as mentioned above, makes sense to dedicate an install to Pico because of an unusual setting, next papa. <br />
<br />
Start a new Lazarus project in the usual way, to keep life simple, save it in a directory under 'projects' in Michael's directory tree. <br />
Firstly, a one off for this Lazarus instance, note this is NOT saved with the project. Open Options -> Files and look for the "Compiler executable" entry. Put in, with a full path, 'fpc.bash'. eg /home/YOUR-USER-NAME/bin/FPC/fpc-3.3.1/bin/fpc.bash (note fpcupdeluxe uses fpc.sh)<br />
<br />
Then, open the Project Options and set the following -<br />
<br />
Paths<br />
Other unit files (-Fu): ../pico-fpcexamples/units<br />
Include files (-Fi) : $(ProjOutDir)<br />
Libraries (-Fl);<br />
Unit output directory (-FU): lib/$(TargetCPU)-$(TargetOS)<br />
Target file name (-o) : bin/project1<br />
[x] Apply conventions<br />
<br />
Config and Target<br />
[x] Use standard compiler config...<br />
[ ] Use additional<br />
Target OS (-T) : Embedded<br />
Target CPU family (-P) : arm<br />
Target processor (-Cp) : ARMV6M<br />
<br />
Custom Options<br />
-Wpraspi_pico<br />
-godwarfsets<br />
-godwarfcpp<br />
-Xu<br />
-XParm-none-eabi-<br />
<br />
Note the last of the Custom Options, when you installed binutils-arm-none-eabi, you added a set of binutils for this particular processor. Some systems may call them arm-embedded-??? if yours uses that name scheme, change that Option accordingly.<br />
<br />
===Compile from Commandline===<br />
Its also reasonable to build from the command line, here is an example -<br />
<br />
#!/bin/bash<br />
echo "------- Using the fpc from 3.3.1 ---------"<br />
export MICHAELS_UNITS="$HOME"/Pico2/projects/pico-fpcexamples/units <br />
"$HOME"/bin/FPC/fpc-3.3.1/bin/fpc.bash -va -Xu -XParm-none-eabi- -Tembedded -Parm -CpARMV6M -Fu"$MICHAELS_UNITS" -Wpraspi_pico project1<br />
<br />
Note: -XParm-none-eabi- indicates how the binutils are named. If yours are named differently, change it !</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Building_a_Pico_Compiler&diff=158374Building a Pico Compiler2024-03-08T10:03:44Z<p>Dbannon: link to example code</p>
<hr />
<div>=Building a Pico Compiler=<br />
<br />
One of FPC's great features is its ability to provide cross compilers. A cross compiler allows you to run on one system and make a binary for another, possibly completely different system. When making binaries for embedded targets, its the only way to go !<br />
<br />
For most users, the most convenient way to get a cross compiler for the Raspberry Pi Pico is to use **fpcupdeluxe** but for those who want to build their own, or just understand the process, here is an example. These instructions focus on using a 64bit Linux system as the host, Windows and perhaps Mac users can probably do something similar.<br />
<br />
===The constraints===<br />
* If you are a Lazarus user, it makes sense to have a stand alone Lazarus install for your Pico work, you could switch a single install (by changing Tools->Options->Files->Compiler_executable) but it could get annoying. Multiple installs means installing Lazarus from source, remarkably easy.<br />
* If you already have a Source install of FPC main December 2023 or later, you can use that, with some care. Again, might be easier and safer to have a dedicated version. You do need an existing install of FPC322, if yours is installed in read only space (installed from a deb or rpm) then I suggest you work here with with a fresh copy of FPC and "install" it into your own space. <br />
<br />
===Dependencies===<br />
* An existing, working, FPC322. <br />
* Install binutils-arm-none-eabi - that means ones for arm, no operating system (or embedded) and eabi indicates the arm subtype, no 'hf' means 'soft float'. <br />
* And, of course, you need Michael's git repo with pico-fpcexamples. These come from https://github.com/michael-ring/pico-fpcexamples Note that Michael's repo contain tow separate things, some nicely set example projects and, in 'units' a number of libraries (*.obj) and pascal units to link them into a pascal project.<br />
<br />
===The Process===<br />
Here are a series of command lines, I do not recommend you run them as a script (although I have) because you MUST check for errors at each step. I keep this in my home directory in a structure like this, change it of course but change the commands if you do ! The commands here will create two fpc-3.3.1 directories, if you have them already, ensure they are empty or change the commands. Note that I have already downloaded fpc.zip to my Downloads dir.<br />
<br />
===Assumed Dir Structure===<br />
$HOME/bin/FPC<br />
fpc-3.2.2 .....<br />
fpc-3.3.1<br />
SRC<br />
fpc-3.2.2 ....<br />
fpc-3.3.1<br />
<br />
=====The commands=====<br />
<br />
#!/bin/bash<br />
<br />
export FPC_VER="fpc-3.3.1" # note 1<br />
export SRC_DIR="$HOME/bin/FPC/SRC/$FPC_VER"<br />
export FPC_DIR="$HOME/bin/FPC/$FPC_VER"<br />
mkdir -p "$FPC_DIR"<br />
mkdir -p "$SRC_DIR"<br />
<br />
cd "$SRC_DIR"<br />
unzip ~/Downloads/fpc.zip # note 2<br />
cd fpc<br />
make all # note 3<br />
<br />
make install INSTALL_PREFIX="$FPC_DIR"<br />
<br />
# note 4<br />
make crossinstall CPU_TARGET=arm OS_TARGET=embedded SUBARCH=armv6m CROSSBINDIR=/usr/bin BINUTILSPREFIX=arm-none-eabi- CROSSOPT="-CpARMV6M -CaEABI" INSTALL_PREFIX="$FPC_DIR"<br />
<br />
<br />
cp compiler/ppcx64 "$FPC_DIR"/bin/ppcx64 # note 5 <br />
cp compiler/ppcrossarm "$FPC_DIR"/bin/ppcrossarm # note 6<br />
<br />
# note 7<br />
cp rtl/embedded/arm/* "$FPC_DIR"/lib/fpc/3.3.1/units/arm-embedded/rtl/.<br />
<br />
cd "$FPC_DIR"<br />
mkdir -p etc<br />
bin/fpcmkcfg -d basepath="$FPC_DIR" -o etc/fpc.cfg # note 8<br />
ln -s "$FPC_DIR"/lib/fpc/3.3.1/units units # note 9<br />
<br />
<br />
cd bin<br />
echo "#!/bin/sh" > fpc.bash # note 10<br />
echo "# A script that ensure when we start fpc here, it ignores all other fpc.cfg" >> fpc.bash<br />
echo "$FPC_DIR""/bin/fpc -n @$FPC_DIR/etc/fpc.cfg \"\$@\"" >> fpc.bash<br />
chmod u+x fpc.bash<br />
<br />
note 1 - right now (March 2024) fpc main thinks of it self as 3.3.1, this may change after the next release of FPC and its possible the necessary updates to make this all work will not be in the release.<br />
<br />
note 2 - make sure you have that fpc.zip file there, main Dec 2024 or later.<br />
<br />
note 3 - this takes a while.<br />
<br />
note 4 - this takes a while too.<br />
<br />
note 5 - this is your host system compiler.<br />
<br />
note 6 - and this is the cross compiler, we call fpc and it calls this one.<br />
<br />
note 7 - these are some units that the RTL, built earlier, will need.<br />
<br />
note 8 - this creates a pretty stock standard fpc.cfg file. <br />
<br />
note 9 - we link the RTL files we made earlier to where the std config file expects them to be.<br />
<br />
note 10 - this makes a script that calls fpc and passes parameters that ensure fpc takes no regard of any other fpc.cfg file, only the one we just made. <br />
<br />
===Setup a Lazarus project===<br />
<br />
Doing your Pico development in Lazarus does make things simpler once setup. You can use (some of) Lazarus's help and code tools systems, click compile (but obviously not run) and have all your errors nicely presented. You do not need to make any changes to Lazarus (apart from settings mentioned below), just about any recent Lazarus will do. But, as mentioned above, makes sense to dedicate an install to Pico because of an unusual setting, next papa. <br />
<br />
Start a new Lazarus project in the usual way, to keep life simple, save it in a directory under 'projects' in Michael's directory tree. <br />
Firstly, a one off for this Lazarus instance, note this is NOT saved with the project. Open Options -> Files and look for the "Compiler executable" entry. Put in, with a full path, 'fpc.bash'. eg /home/YOUR-USER-NAME/bin/FPC/fpc-3.3.1/bin/fpc.bash (note fpcupdeluxe uses fpc.sh)<br />
<br />
Then, open the Project Options and set the following -<br />
<br />
Paths<br />
Other unit files (-Fu): ../pico-fpcexamples/units<br />
Include files (-Fi) : $(ProjOutDir)<br />
Libraries (-Fl);<br />
Unit output directory (-FU): lib/$(TargetCPU)-$(TargetOS)<br />
Target file name (-o) : bin/project1<br />
[x] Apply conventions<br />
<br />
Config and Target<br />
[x] Use standard compiler config...<br />
[ ] Use additional<br />
Target OS (-T) : Embedded<br />
Target CPU family (-P) : arm<br />
Target processor (-Cp) : ARMV6M<br />
<br />
Custom Options<br />
-Wpraspi_pico<br />
-godwarfsets<br />
-godwarfcpp<br />
-Xu<br />
-XParm-none-eabi-<br />
<br />
Note the last of the Custom Options, when you installed binutils-arm-none-eabi, you added a set of binutils for this particular processor. Some systems may call them arm-embedded-??? if yours uses that name scheme, change that Option accordingly.<br />
<br />
===Compile from Commandline===<br />
Its also reasonable to build from the command line, here is an example -<br />
<br />
#!/bin/bash<br />
echo "------- Using the fpc from 3.3.1 ---------"<br />
export MICHAELS_UNITS="$HOME"/Pico2/projects/pico-fpcexamples/units <br />
"$HOME"/bin/FPC/fpc-3.3.1/bin/fpc.bash -va -Xu -XParm-none-eabi- -Tembedded -Parm -CpARMV6M -Fu"$MICHAELS_UNITS" -Wpraspi_pico project1</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Building_a_Pico_Compiler&diff=158373Building a Pico Compiler2024-03-08T09:59:14Z<p>Dbannon: command line</p>
<hr />
<div>=Building a Pico Compiler=<br />
<br />
One of FPC's great features is its ability to provide cross compilers. A cross compiler allows you to run on one system and make a binary for another, possibly completely different system. When making binaries for embedded targets, its the only way to go !<br />
<br />
For most users, the most convenient way to get a cross compiler for the Raspberry Pi Pico is to use **fpcupdeluxe** but for those who want to build their own, or just understand the process, here is an example. These instructions focus on using a 64bit Linux system as the host, Windows and perhaps Mac users can probably do something similar.<br />
<br />
===The constraints===<br />
* If you are a Lazarus user, it makes sense to have a stand alone Lazarus install for your Pico work, you could switch a single install (by changing Tools->Options->Files->Compiler_executable) but it could get annoying. Multiple installs means installing Lazarus from source, remarkably easy.<br />
* If you already have a Source install of FPC main December 2023 or later, you can use that, with some care. Again, might be easier and safer to have a dedicated version. You do need an existing install of FPC322, if yours is installed in read only space (installed from a deb or rpm) then I suggest you work here with with a fresh copy of FPC and "install" it into your own space. <br />
<br />
===Dependencies===<br />
* An existing, working, FPC322. <br />
* Install binutils-arm-none-eabi - that means ones for arm, no operating system (or embedded) and eabi indicates the arm subtype, no 'hf' means 'soft float'. <br />
* And, of course, you need Michael's git repo with pico-fpcexamples.<br />
<br />
===The Process===<br />
Here are a series of command lines, I do not recommend you run them as a script (although I have) because you MUST check for errors at each step. I keep this in my home directory in a structure like this, change it of course but change the commands if you do ! The commands here will create two fpc-3.3.1 directories, if you have them already, ensure they are empty or change the commands. Note that I have already downloaded fpc.zip to my Downloads dir.<br />
<br />
===Assumed Dir Structure===<br />
$HOME/bin/FPC<br />
fpc-3.2.2 .....<br />
fpc-3.3.1<br />
SRC<br />
fpc-3.2.2 ....<br />
fpc-3.3.1<br />
<br />
=====The commands=====<br />
<br />
#!/bin/bash<br />
<br />
export FPC_VER="fpc-3.3.1" # note 1<br />
export SRC_DIR="$HOME/bin/FPC/SRC/$FPC_VER"<br />
export FPC_DIR="$HOME/bin/FPC/$FPC_VER"<br />
mkdir -p "$FPC_DIR"<br />
mkdir -p "$SRC_DIR"<br />
<br />
cd "$SRC_DIR"<br />
unzip ~/Downloads/fpc.zip # note 2<br />
cd fpc<br />
make all # note 3<br />
<br />
make install INSTALL_PREFIX="$FPC_DIR"<br />
<br />
# note 4<br />
make crossinstall CPU_TARGET=arm OS_TARGET=embedded SUBARCH=armv6m CROSSBINDIR=/usr/bin BINUTILSPREFIX=arm-none-eabi- CROSSOPT="-CpARMV6M -CaEABI" INSTALL_PREFIX="$FPC_DIR"<br />
<br />
<br />
cp compiler/ppcx64 "$FPC_DIR"/bin/ppcx64 # note 5 <br />
cp compiler/ppcrossarm "$FPC_DIR"/bin/ppcrossarm # note 6<br />
<br />
# note 7<br />
cp rtl/embedded/arm/* "$FPC_DIR"/lib/fpc/3.3.1/units/arm-embedded/rtl/.<br />
<br />
cd "$FPC_DIR"<br />
mkdir -p etc<br />
bin/fpcmkcfg -d basepath="$FPC_DIR" -o etc/fpc.cfg # note 8<br />
ln -s "$FPC_DIR"/lib/fpc/3.3.1/units units # note 9<br />
<br />
<br />
cd bin<br />
echo "#!/bin/sh" > fpc.bash # note 10<br />
echo "# A script that ensure when we start fpc here, it ignores all other fpc.cfg" >> fpc.bash<br />
echo "$FPC_DIR""/bin/fpc -n @$FPC_DIR/etc/fpc.cfg \"\$@\"" >> fpc.bash<br />
chmod u+x fpc.bash<br />
<br />
note 1 - right now (March 2024) fpc main thinks of it self as 3.3.1, this may change after the next release of FPC and its possible the necessary updates to make this all work will not be in the release.<br />
<br />
note 2 - make sure you have that fpc.zip file there, main Dec 2024 or later.<br />
<br />
note 3 - this takes a while.<br />
<br />
note 4 - this takes a while too.<br />
<br />
note 5 - this is your host system compiler.<br />
<br />
note 6 - and this is the cross compiler, we call fpc and it calls this one.<br />
<br />
note 7 - these are some units that the RTL, built earlier, will need.<br />
<br />
note 8 - this creates a pretty stock standard fpc.cfg file. <br />
<br />
note 9 - we link the RTL files we made earlier to where the std config file expects them to be.<br />
<br />
note 10 - this makes a script that calls fpc and passes parameters that ensure fpc takes no regard of any other fpc.cfg file, only the one we just made. <br />
<br />
===Setup a Lazarus project===<br />
<br />
Doing your Pico development in Lazarus does make things simpler once setup. You can use (some of) Lazarus's help and code tools systems, click compile (but obviously not run) and have all your errors nicely presented. You do not need to make any changes to Lazarus (apart from settings mentioned below), just about any recent Lazarus will do. But, as mentioned above, makes sense to dedicate an install to Pico because of an unusual setting, next papa. <br />
<br />
Start a new Lazarus project in the usual way, to keep life simple, save it in a directory under 'projects' in Michael's directory tree. <br />
Firstly, a one off for this Lazarus instance, note this is NOT saved with the project. Open Options -> Files and look for the "Compiler executable" entry. Put in, with a full path, 'fpc.bash'. eg /home/YOUR-USER-NAME/bin/FPC/fpc-3.3.1/bin/fpc.bash (note fpcupdeluxe uses fpc.sh)<br />
<br />
Then, open the Project Options and set the following -<br />
<br />
Paths<br />
Other unit files (-Fu): ../pico-fpcexamples/units<br />
Include files (-Fi) : $(ProjOutDir)<br />
Libraries (-Fl);<br />
Unit output directory (-FU): lib/$(TargetCPU)-$(TargetOS)<br />
Target file name (-o) : bin/project1<br />
[x] Apply conventions<br />
<br />
Config and Target<br />
[x] Use standard compiler config...<br />
[ ] Use additional<br />
Target OS (-T) : Embedded<br />
Target CPU family (-P) : arm<br />
Target processor (-Cp) : ARMV6M<br />
<br />
Custom Options<br />
-Wpraspi_pico<br />
-godwarfsets<br />
-godwarfcpp<br />
-Xu<br />
-XParm-none-eabi-<br />
<br />
Note the last of the Custom Options, when you installed binutils-arm-none-eabi, you added a set of binutils for this particular processor. Some systems may call them arm-embedded-??? if yours uses that name scheme, change that Option accordingly.<br />
<br />
===Compile from Commandline===<br />
Its also reasonable to build from the command line, here is an example -<br />
<br />
#!/bin/bash<br />
echo "------- Using the fpc from 3.3.1 ---------"<br />
export MICHAELS_UNITS="$HOME"/Pico2/projects/pico-fpcexamples/units <br />
"$HOME"/bin/FPC/fpc-3.3.1/bin/fpc.bash -va -Xu -XParm-none-eabi- -Tembedded -Parm -CpARMV6M -Fu"$MICHAELS_UNITS" -Wpraspi_pico project1</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Building_a_Pico_Compiler&diff=158368Building a Pico Compiler2024-03-08T09:46:47Z<p>Dbannon: first cut</p>
<hr />
<div>=Building a Pico Compiler=<br />
<br />
One of FPC's great features is its ability to provide cross compilers. A cross compiler allows you to run on one system and make a binary for another, possibly completely different system. When making binaries for embedded targets, its the only way to go !<br />
<br />
For most users, the most convenient way to get a cross compiler for the Raspberry Pi Pico is to use **fpcupdeluxe** but for those who want to build their own, or just understand the process, here is an example. These instructions focus on using a 64bit Linux system as the host, Windows and perhaps Mac users can probably do something similar.<br />
<br />
===The constraints===<br />
* If you are a Lazarus user, it makes sense to have a stand alone Lazarus install for your Pico work, you could switch a single install (by changing Tools->Options->Files->Compiler_executable) but it could get annoying. Multiple installs means installing Lazarus from source, remarkably easy.<br />
* If you already have a Source install of FPC main December 2023 or later, you can use that, with some care. Again, might be easier and safer to have a dedicated version. You do need an existing install of FPC322, if yours is installed in read only space (installed from a deb or rpm) then I suggest you work here with with a fresh copy of FPC and "install" it into your own space. <br />
<br />
===Dependencies===<br />
* An existing, working, FPC322. <br />
* Install binutils-arm-none-eabi - that means ones for arm, no operating system (or embedded) and eabi indicates the arm subtype, no 'hf' means 'soft float'. <br />
* And, of course, you need Michael's git repo with pico-fpcexamples.<br />
<br />
===The Process===<br />
Here are a series of command lines, I do not recommend you run them as a script (although I have) because you MUST check for errors at each step. I keep this in my home directory in a structure like this, change it of course but change the commands if you do ! The commands here will create two fpc-3.3.1 directories, if you have them already, ensure they are empty or change the commands. Note that I have already downloaded fpc.zip to my Downloads dir.<br />
<br />
===Assumed Dir Structure===<br />
$HOME/bin/FPC<br />
fpc-3.2.2 .....<br />
fpc-3.3.1<br />
SRC<br />
fpc-3.2.2 ....<br />
fpc-3.3.1<br />
<br />
=====The commands=====<br />
<br />
#!/bin/bash<br />
<br />
export FPC_VER="fpc-3.3.1" # note 1<br />
export SRC_DIR="$HOME/bin/FPC/SRC/$FPC_VER"<br />
export FPC_DIR="$HOME/bin/FPC/$FPC_VER"<br />
mkdir -p "$FPC_DIR"<br />
mkdir -p "$SRC_DIR"<br />
<br />
cd "$SRC_DIR"<br />
unzip ~/Downloads/fpc.zip # note 2<br />
cd fpc<br />
make all # note 3<br />
<br />
make install INSTALL_PREFIX="$FPC_DIR"<br />
<br />
# note 4<br />
make crossinstall CPU_TARGET=arm OS_TARGET=embedded SUBARCH=armv6m CROSSBINDIR=/usr/bin BINUTILSPREFIX=arm-none-eabi- CROSSOPT="-CpARMV6M -CaEABI" INSTALL_PREFIX="$FPC_DIR"<br />
<br />
<br />
cp compiler/ppcx64 "$FPC_DIR"/bin/ppcx64 # note 5 <br />
cp compiler/ppcrossarm "$FPC_DIR"/bin/ppcrossarm # note 6<br />
<br />
# note 7<br />
cp rtl/embedded/arm/* "$FPC_DIR"/lib/fpc/3.3.1/units/arm-embedded/rtl/.<br />
<br />
cd "$FPC_DIR"<br />
mkdir -p etc<br />
bin/fpcmkcfg -d basepath="$FPC_DIR" -o etc/fpc.cfg # note 8<br />
ln -s "$FPC_DIR"/lib/fpc/3.3.1/units units # note 9<br />
<br />
<br />
cd bin<br />
echo "#!/bin/sh" > fpc.bash # note 10<br />
echo "# A script that ensure when we start fpc here, it ignores all other fpc.cfg" >> fpc.bash<br />
echo "$FPC_DIR""/bin/fpc -n @$FPC_DIR/etc/fpc.cfg \"\$@\"" >> fpc.bash<br />
chmod u+x fpc.bash<br />
<br />
note 1 - right now (March 2024) fpc main thinks of it self as 3.3.1, this may change after the next release of FPC and its possible the necessary updates to make this all work will not be in the release.<br />
<br />
note 2 - make sure you have that fpc.zip file there, main Dec 2024 or later.<br />
<br />
note 3 - this takes a while.<br />
<br />
note 4 - this takes a while too.<br />
<br />
note 5 - this is your host system compiler.<br />
<br />
note 6 - and this is the cross compiler, we call fpc and it calls this one.<br />
<br />
note 7 - these are some units that the RTL, built earlier, will need.<br />
<br />
note 8 - this creates a pretty stock standard fpc.cfg file. <br />
<br />
note 9 - we link the RTL files we made earlier to where the std config file expects them to be.<br />
<br />
note 10 - this makes a script that calls fpc and passes parameters that ensure fpc takes no regard of any other fpc.cfg file, only the one we just made. <br />
<br />
===Setup a Lazarus project===<br />
<br />
Doing your Pico development in Lazarus does make things simpler once setup. You can use (some of) Lazarus's help and code tools systems, click compile (but obviously not run) and have all your errors nicely presented. You do not need to make any changes to Lazarus (apart from settings mentioned below), just about any recent Lazarus will do. But, as mentioned above, makes sense to dedicate an install to Pico because of an unusual setting, next papa. <br />
<br />
Start a new Lazarus project in the usual way, to keep life simple, save it in a directory under 'projects' in Michael's directory tree. <br />
Firstly, a one off for this Lazarus instance, note this is NOT saved with the project. Open Options -> Files and look for the "Compiler executable" entry. Put in, with a full path, 'fpc.bash'. eg /home/YOUR-USER-NAME/bin/FPC/fpc-3.3.1/bin/fpc.bash (note fpcupdeluxe uses fpc.sh)<br />
<br />
Then, open the Project Options and set the following -<br />
<br />
Paths<br />
Other unit files (-Fu): ../pico-fpcexamples/units<br />
Include files (-Fi) : $(ProjOutDir)<br />
Libraries (-Fl);<br />
Unit output directory (-FU): lib/$(TargetCPU)-$(TargetOS)<br />
Target file name (-o) : bin/project1<br />
[x] Apply conventions<br />
<br />
Config and Target<br />
[x] Use standard compiler config...<br />
[ ] Use additional<br />
Target OS (-T) : Embedded<br />
Target CPU family (-P) : arm<br />
Target processor (-Cp) : ARMV6M<br />
<br />
Custom Options<br />
-Wpraspi_pico<br />
-godwarfsets<br />
-godwarfcpp<br />
-Xu<br />
-XParm-none-eabi-<br />
<br />
Note the last of the Custom Options, when you installed binutils-arm-none-eabi, you added a set of binutils for this particular processor. Some systems may call them arm-embedded-??? if yours uses that name scheme, change that Option accordingly.</div>Dbannonhttps://wiki.freepascal.org/index.php?title=ARM_Embedded_Tutorial_-_FPC_and_the_Raspberry_Pi_Pico&diff=158365ARM Embedded Tutorial - FPC and the Raspberry Pi Pico2024-03-08T09:31:16Z<p>Dbannon: added link to building page</p>
<hr />
<div>{{ARM Embedded Tutorial - FPC and the Raspberry Pi Pico}}<br />
<br />
The Raspberry Pi Foundation has released the Raspberry Pi Pico, a very cheap Microcontroller board with quite interesting specs.<br />
<br />
===Introduction===<br />
<br />
[https://www.raspberrypi.org/products/raspberry-pi-pico/specifications/ Raspberry Pi Pico Specifications]<br />
<br />
To best use this tutorial you will need to buy (at least) two Raspberry Pi Pico, we will use one as a target and the second one as a debug probe. Do yourself a favour, invest $4 for a second device, being able to debug is worth so much more.<br />
<br />
As the Pico is brand new and support for the board is a work in progress I'd recommend that you set up a dedicated installation of Lazarus and Free Pascal as you will need to use both trunk version of Lazarus and a specially patched version of FPC that includes the necessary adjustments so that FPC knows about the Pico. Also expect changes as we all learn along the way.<br />
<br />
<br />
===Setup===<br />
To install the required versions of Lazarus and Free Pascal please see here:<br />
<br />
[[ARM Embedded Tutorial - Installing Lazarus and Free Pascal]]<br />
<br />
<br />
===PicoProbe===<br />
To prepare a PicoProbe and to setup Lazarus please follow this guide:<br />
<br />
[[ARM Embedded Tutorial - Raspberry Pi Pico Setting up for Development]]<br />
<br />
===The Examples from Github===<br />
To access the examples together with all needed dependencies clone this repository:<br />
<br />
https://github.com/michael-ring/pico-fpcexamples<br />
<br />
when you find errors in the code or would like to request another demo please enter an issue on github:<br />
<br />
https://github.com/michael-ring/pico-fpcexamples/issues<br />
<br />
===Example Programs===<br />
Now we are ready for our first Program, as practice in the embedded programming world we start with blinking the on-board LED:<br />
<br />
=====Blinky=====<br />
[[ARM Embedded Tutorial - Raspberry Pi Pico Blinking the onboard LED]]<br />
Not suitable for the Raspberry Pi W<br />
<br />
=====Debugging=====<br />
The next step in this tutorial is to set up Debugging from within Lazarus<br />
<br />
[[ARM Embedded Tutorial - Raspberry Pi Pico Debugging the onboard LED]]<br />
<br />
The next peripheral to join the party is the UART:<br />
<br />
[[ARM Embedded Tutorial - Raspberry Pi Pico saying Hello via UART]]<br />
<br />
=====ADC=====<br />
<br />
Time to go Analog:<br />
<br />
[[ARM Embedded Tutorial - Raspberry Pi Pico using the ADC]]<br />
<br />
=====I2C=====<br />
Scanning the I2C Bus for Devices:<br />
<br />
[[ARM Embedded Tutorial - Raspberry Pi Pico Scanning for I2C Devices]]<br />
<br />
Talking to a Display via I2C:<br />
<br />
[[ARM Embedded Tutorial - Raspberry Pi Pico using Displays and I2C]]<br />
<br />
=====SPI=====<br />
Talking to a Display via SPI:<br />
<br />
[[ARM Embedded Tutorial - Raspberry Pi Pico using Displays and SPI]]<br />
<br />
<br />
===Building a Pico Compiler===<br />
As indicated about, the most common way to get a working Pico compiler is to use fpcupdeluxe but you can, of course, build it yourself [[Building a Pico Compiler]]. <br />
<br />
===Finally===<br />
This page is WIP, I received my Boards on 29.01.2021, upgrading it as I go....</div>Dbannonhttps://wiki.freepascal.org/index.php?title=ARM_Embedded_Tutorial_-_FPC_and_the_Raspberry_Pi_Pico&diff=158364ARM Embedded Tutorial - FPC and the Raspberry Pi Pico2024-03-08T09:22:10Z<p>Dbannon: A bit of formatting</p>
<hr />
<div>{{ARM Embedded Tutorial - FPC and the Raspberry Pi Pico}}<br />
<br />
The Raspberry Pi Foundation has released the Raspberry Pi Pico, a very cheap Microcontroller board with quite interesting specs.<br />
<br />
===Introduction===<br />
<br />
[https://www.raspberrypi.org/products/raspberry-pi-pico/specifications/ Raspberry Pi Pico Specifications]<br />
<br />
To best use this tutorial you will need to buy (at least) two Raspberry Pi Pico, we will use one as a target and the second one as a debug probe. Do yourself a favour, invest $4 for a second device, being able to debug is worth so much more.<br />
<br />
As the Pico is brand new and support for the board is a work in progress I'd recommend that you set up a dedicated installation of Lazarus and Free Pascal as you will need to use both trunk version of Lazarus and a specially patched version of FPC that includes the necessary adjustments so that FPC knows about the Pico. Also expect changes as we all learn along the way.<br />
<br />
<br />
===Setup===<br />
To install the required versions of Lazarus and Free Pascal please see here:<br />
<br />
[[ARM Embedded Tutorial - Installing Lazarus and Free Pascal]]<br />
<br />
<br />
===PicoProbe===<br />
To prepare a PicoProbe and to setup Lazarus please follow this guide:<br />
<br />
[[ARM Embedded Tutorial - Raspberry Pi Pico Setting up for Development]]<br />
<br />
===The Examples from Github===<br />
To access the examples together with all needed dependencies clone this repository:<br />
<br />
https://github.com/michael-ring/pico-fpcexamples<br />
<br />
when you find errors in the code or would like to request another demo please enter an issue on github:<br />
<br />
https://github.com/michael-ring/pico-fpcexamples/issues<br />
<br />
===Example Programs===<br />
Now we are ready for our first Program, as practice in the embedded programming world we start with blinking the on-board LED:<br />
<br />
=====Blinky=====<br />
[[ARM Embedded Tutorial - Raspberry Pi Pico Blinking the onboard LED]]<br />
Not suitable for the Raspberry Pi W<br />
<br />
=====Debugging=====<br />
The next step in this tutorial is to set up Debugging from within Lazarus<br />
<br />
[[ARM Embedded Tutorial - Raspberry Pi Pico Debugging the onboard LED]]<br />
<br />
The next peripheral to join the party is the UART:<br />
<br />
[[ARM Embedded Tutorial - Raspberry Pi Pico saying Hello via UART]]<br />
<br />
=====ADC=====<br />
<br />
Time to go Analog:<br />
<br />
[[ARM Embedded Tutorial - Raspberry Pi Pico using the ADC]]<br />
<br />
=====I2C=====<br />
Scanning the I2C Bus for Devices:<br />
<br />
[[ARM Embedded Tutorial - Raspberry Pi Pico Scanning for I2C Devices]]<br />
<br />
Talking to a Display via I2C:<br />
<br />
[[ARM Embedded Tutorial - Raspberry Pi Pico using Displays and I2C]]<br />
<br />
=====SPI=====<br />
Talking to a Display via SPI:<br />
<br />
[[ARM Embedded Tutorial - Raspberry Pi Pico using Displays and SPI]]<br />
<br />
<br />
<br />
This page is WIP, I received my Boards on 29.01.2021, upgrading it as I go....</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Installing_the_Free_Pascal_Compiler&diff=158342Installing the Free Pascal Compiler2024-03-04T02:49:13Z<p>Dbannon: dev versions of fpc</p>
<hr />
<div>{{LanguageBar}}<br />
<br />
While the Free Pascal Compiler comes pre-built for several processor architectures and operating systems, there may be occasions where a pre-built compiler won't do, and you need to build it yourself. There are a number of reasons for doing this, including: <br />
* Intellectual curiosity - you want to discover the compiler's speed, using a medium sized application like the compiler itself.<br />
* Cross Compiling - you want to build a compiler that creates binaries for a different CPU architecture, operating environment, or both, than the one you are using right now.<br />
* New environment - you want to build the compiler for a processor or operating system that either a version of the compiler does not currently exist, or you are porting it to a new operating system or processor.<br />
* Do it yourself - you prefer to build applications from source where possible.<br />
* Security - for organizational policy, legal requirements, or operational security, programs from untrusted sources cannot be used, and must be recompiled locally. Or you want to confirm the compiler you have is the same as the one it produces, possibly running a checksum on the new one to confirm it matches the published one.<br />
And the most obvious reason of all:<br />
* New Version - you are creating a new release of the compiler.<br />
<br />
This document will try to help you do these things.<br />
<br />
== Overview ==<br />
<br />
There are a number of ways to install the Free Pascal Compiler and its libraries on your system. The current version and trunk, and sometimes beta and release candidates, are available directly from the [https://sourceforge.net/projects/freepascal/files/ FPC website] (tar, exe, rpm, dmg, pkg files). In addition, versions of FPC are provided (including rpm and deb files) from the [http://sourceforge.net/projects/lazarus/files/ Lazarus download site]. Linux users can almost certainly find FPC in their distribution's repositories but may find that it is outdated. <br />
<br />
It is possible to build FPC from source, typically each release needs to be built with the previous release. It is possible to install FPC in the application space (often requiring root or administrator access) or in a your own user space. These things depend on your particular operating system.<br />
<br />
There is an extensive discussion of how to install and build Free Pascal compilers available here http://www.stack.nl/~marcov/buildfaq.pdf - it may be a little too detailed for some users, but it is very comprehensive.<br />
<br />
== Linux ==<br />
<br />
=== Downloading FPC binary packages ===<br />
<br />
{{Note|You have a number of way to install FPC presented here, which one is best for you depends very much on what you are planning to do and if you need a very current version. If you are also planning on installing Lazarus, perhaps a quick look at [[Installing_Lazarus]] and [[Installing_Lazarus_on_Linux]] is a very good idea. }} <br />
<br />
With the recent release of FPC 3.2.2, only a few distributions will have the new FPC compiler in its repositories. You can do one of -<br />
<br />
* If you use '''rpm packages''', you can find FPC322 in https://sourceforge.net/projects/freepascal/files/Linux/3.2.2/ or more generally in https://www.freepascal.org/download.html You might install using a command like -<syntaxhighlight lang="bash">yum localinstall fpc-3.2.2-1.x86_64.rpm<br />
// or perhaps<br />
rpm -Uvh fpc-3.2.2-1.x86_64.rpm</syntaxhighlight><br />
* If you use '''deb packages''' try the ones made available by, but not bundled with Lazarus at https://sourceforge.net/projects/lazarus/files/. Install using gdebi or, perhaps this command (note the ./ in front of filename, if you don't use an explicit path to filename, apt will search its own official repo, not what you want)-<syntaxhighlight lang="bash">apt install ./fpc-laz_3.2.2-1_amd64.deb </syntaxhighlight> Note the actual name changes from time to time, use the name you downloaded !<br />
<br />
If you already have a FPC installed from your distro package manager, you should use that same package manager to remove them before installing under another model. Some quite strange things can happen if your compile uses a mixture of FPC versions !<br />
<br />
=== Downloading FPC tar balls ===<br />
<br />
Another option is installing from a tar, an easy and possibly even more useful model. These tars are available for a wide range of Unix like systems. You need to download the appropriate binary tar for your OS and CPU and you may also need the source files, common for all OS/CPU. You install FPC this way in your own space, not as root.<br />
<br />
Here is a series of commands, not a script, that will install FPC on a 64 bit Linux system. It could be scripted but would would need sanity and error checking. Note that I like to install things like this in my $HOME/bin directory, if you prefer having it in $HOME, it is even easier, I am sure you can see the differences.<br />
<br />
{{Note| A tar install '''does not resolve dependencies''', if they are not already present, first install binutils, make, gcc. }}<br />
<br />
{{Note| A tar ball install will, if you manage paths properly, allow you to have several versions of FPC installed.}}<br />
<br />
(if you want something a bit newer, please see https://www.freepascal.org/develop.html )<br />
<br />
<syntaxhighlight lang="bash"><br />
cd<br />
mkdir -p bin/FPC/Tars<br />
mkdir bin/FPC/SRC<br />
mkdir bin/FPC/fpc-3.2.2<br />
cd bin/FPC/Tars<br />
# download src and compiler tars, change for different CPU (or Mac ?) <br />
<br />
wget https://sourceforge.net/projects/freepascal/files/Source/3.2.2/fpc-3.2.2.source.tar.gz<br />
wget https://sourceforge.net/projects/freepascal/files/Linux/3.2.2/fpc-3.2.2.x86_64-linux.tar<br />
<br />
tar xf fpc-3.2.2.x86_64-linux.tar<br />
cd fpc-3.2.2.x86_64-linux<br />
./install.sh<br />
# when asked where to install, enter $HOME/bin/FPC/fpc-3.2.2, accept all defaults after that.<br />
<br />
cd ../../SRC<br />
tar xzf ../Tars/fpc-3.2.2.source.tar.gz<br />
<br />
# Set a path to where the compiler is, add line at end of .bashrc, if you don't use bash, adjust !<br />
# (! e.g. Use ~/.profile instead of ~/.bashrc so that the path is available also in Lazarus IDE.)<br />
echo "PATH=\"\$HOME/bin/FPC/fpc-3.2.2/bin\":\"\$PATH\"" >> ~/.bashrc<br />
source ~/.bashrc<br />
<br />
# trivial test<br />
cd <br />
fpc -iV<br />
</syntaxhighlight><br />
<br />
{{Note|The second wget above assumes you are working with an amd64 (AMD or Intel) type machine, other platforms require a different tar ball. For example, for a '''Raspberry Pi''', you would use - <syntaxhighlight lang=bash>wget https://sourceforge.net/projects/freepascal/files/Linux/3.2.2/fpc-3.2.2.arm-linux.tar</syntaxhighlight> which has the armhf 32bit compiler. Incidentally, the Raspberry Pi may need you to increase swap space to at least a Gig if you plan to later build any large applications with FPC.}}<br />
<br />
=== Downloading FPC sources from Git or zip ===<br />
<br />
If you plan to build your own compiler from source, this is the approach for you.<br />
<br />
The FPC source files are stored in a git repository (https://gitlab.com/freepascal.org/fpc/source). The repository keeps track of all the changes by developers in the source tree and has multiple branches. Once you have the sources, please see [[#Installing_from_source_on_BSD.2FLinux|Installing from source under BSD/Linux]] for instructions on how to install them.<br />
<br />
Anyone can download (but not upload) the source using either git or, easier if you want a one off snapshot, zip. And possibly even SVN. The default is to get the main (aka trunk) version of the source but you also select any one of the very many branches or tags.<br />
<br />
'''Git''' - is most appropriate if you are going to add or sorrect something in the source and submit your change to the developers. Its a bigger and slower download but once done, easy to keep up to date. Note that its very unlikely contributions to anything other than main will be accepted. Here is an example initial download of main, assuming you already have git installed -<br />
<br />
git clone https://gitlab.com/freepascal.org/fpc/source<br />
<br />
Once the git download is finished, cd into the new directory and build as documented below. You can now keep your git repository up to date using the normal git commands. Git is well documented online but (https://wiki.freepascal.org/FPC_git) has some fpc specific help.<br />
<br />
'''Zip''' - a possibly easier option is to download a "zip ball' archive of the source, from the web interface you can select which branch or tag you are interested in (combo box that defaults to 'main'). There are quite a lot of branches and tags to choose from, so please choose carefully. The web interface will let you download the zip of the selected branch or tag directly or command line people may do one of (with the last part of command line indicating which branch):<br />
<br />
wget https://gitlab.com/freepascal.org/fpc/source/-/archive/fixes_3_2/source-fixes_3_2.zip<br />
wget https://gitlab.com/freepascal.org/fpc/source/-/archive/release_3_2_2/source-release_3_2_2.zip<br />
<br />
You would unzip the zipball and build the source as described below.<br />
<br />
==== Documentation sources ====<br />
<br />
The documentation sources are in a separate repository called fpcdocs, so the command to get them is:<br />
<br />
<syntaxhighlight lang="bash"><br />
cd ~<br />
git clone https://gitlab.com/freepascal.org/fpc/documentation.git fpcdocs<br />
</syntaxhighlight><br />
<br />
If you want to learn more about subversion, read this excellent [http://svnbook.red-bean.com/ Subversion book] which is also available online in different formats for free.<br />
<br />
For more information, see the [https://www.freepascal.org/develop.html Free Pascal website].<br />
<br />
=== Compiling the FPC source, using preinstalled FPC ===<br />
<br />
* First install the release FPC from RPM/DEB or tarballs as described above.<br />
* Get FPC sources (trunk) as described above.<br />
* Make FPC sources with (execute in the source (trunk) directory):<br />
<syntaxhighlight lang="bash"><br />
make clean<br />
make all<br />
make install INSTALL_PREFIX=install/to/directory<br />
</syntaxhighlight><br />
Decide about install/to/directory and also add options that you need.<br />
* Check your ~/.fpc.cfg or /etc/fpc.cfg. It either has to be valid for the trunk compiler as well or you have to move it to the ../etc directory above your compiler executable. E.g. if your compiler is in ~/software/fpc/3.2.0/lib/fpc/3.2.0 (~/software/fpc/3.2.0/bin), you have to move the fpc.cfg to ~/software/fpc/3.2.0/lib/fpc/etc.<br />
* If you have no valid global fpc.cfg file, you have to create one in install/to/directory/lib/fpc/etc. For this run the following commands<br />
<syntaxhighlight lang="bash"><br />
cd install/to/directory/lib/fpc<br />
mkdir etc<br />
fpcmkcfg -d basepath="install/to/directory/lib/fpc/\$fpcversion" -o etc/fpc.cfg<br />
</syntaxhighlight><br />
<br />
=== Compiling the FPC source, using just downloaded FPC 3.2.2 ===<br />
* Download FPC 3.2.2 (latest official release), ie tarball fpc-3.2.2.x86_64-linux.tar.<br />
* Unpack this tarball to e.g. "~/Downloads/fpc", do CD to the folder containing the file "install.sh", run from Terminal:<br />
<br />
$ ./install.sh<br />
<br />
It will ask several questions in the Terminal, answer them. You will have installed FPC 3.2.2 to the folder e.g. "/home/user/fpc-3.2.2".<br />
<br />
* Download the Git snapshot of FPC:<br />
<br />
$ git clone https://gitlab.com/freepascal.org/fpc/source<br />
<br />
* Do CD to new folder "source" and call:<br />
<br />
$ make clean PP=/home/user/fpc-3.2.2/bin/fpc<br />
$ make all PP=/home/user/fpc-3.2.2/bin/fpc<br />
<br />
Here you must specify the "PP=" key and the full path to the "fpc" file installed from tarball.<br />
Command "make clean" is required if you '''compile again'''.<br />
<br />
Final compiled binary will be the file "source/compiler/ppcx64".<br />
<br />
== FreeBSD ==<br />
<br />
{{Note|If you wish to also install Lazarus, you can omit installing FPC with the steps below as the Lazarus port will install it for you. See [[Installing Lazarus on FreeBSD]].}}<br />
<br />
=== Installing from the ports collection ===<br />
<br />
The FreeBSD ports collection has FPC v3.2.2 version in <tt>/usr/ports/lang/fpc</tt> and FPC Development v3.3.1 version in <tt>/usr/ports/lang/fpc-devel</tt>. Nowadays all units were merged into only one package of FPC/FPC-devel. The FPC/FPC-devel source is now installed by default; it previously needed to be copied and uncompressed from <tt>/usr/ports/distfiles/freepascal</tt>.<br />
<br />
This must be done as root.<br />
<br />
<syntaxhighlight lang="bash"><br />
# cd /usr/ports/lang/fpc && make install && make clean<br />
</syntaxhighlight><br />
<br />
If you want use development (aka trunk) version of fpc with all units included do the following:<br />
<br />
<syntaxhighlight lang="bash"><br />
# cd /usr/ports/lang/fpc-devel && make install clean<br />
</syntaxhighlight><br />
<br />
Also, you can install FPC and units from pkg binaries:<br />
<br />
<syntaxhighlight lang="bash"><br />
# pkg install lang/fpc<br />
</syntaxhighlight><br />
<br />
Installing development FPC and units from pkg binaries:<br />
<br />
<syntaxhighlight lang="bash"><br />
# pkg install lang/fpc-devel<br />
</syntaxhighlight><br />
<br />
Once FPC is installed you can check if it's working by simply running as a normal user:<br />
<br />
<syntaxhighlight lang="bash"><br />
$ fpc test<br />
</syntaxhighlight><br />
<br />
which should produce output similar to this:<br />
<br />
Free Pascal Compiler version 3.2.2 [2021/08/03] for x86_64<br />
Copyright (c) 1993-2021 by Florian Klaempfl and others<br />
Target OS: FreeBSD for x86-64<br />
Compiling test<br />
Fatal: Cannot open file "test"<br />
Fatal: Compilation aborted<br />
Error: /usr/local/bin/ppcx64 returned an error exitcode<br />
<br />
=== Installing from tar ===<br />
<br />
Select the appropriate 32 bit/64 bit tar file based on your system. For x86_64 64-bit FreeBSD, download from [https://www.freepascal.org/down/x86_64/freebsd.html x86_64]. For i386 32-bit FreeBSD, download from [https://www.freepascal.org/down/i386/freebsd.html i386].<br />
<br />
To install Free Pascal from a terminal:<br />
<br />
<syntaxhighlight lang="bash"><br />
$ fetch ftp://ftp.freepascal.org/pub/fpc/dist/3.2.2/x86_64-freebsd/fpc-3.2.2.x86_64-freebsd11.tar<br />
$ tar -xf fpc-3.2.2.x86_64-freebsd11.tar<br />
$ cd fpc-3.2.2.x86_64-freebsd<br />
$ bash install.sh<br />
</syntaxhighlight><br />
<br />
Replace the desired Free Pascal version (3.0.4, 3.2.0 or 3.2.2) and architecture (x86_64 or i386) as required. <br />
<br />
If you want to install the Free Pascal Compiler globally, for example in <tt>/usr/local</tt>, run the install.sh script as root. <code>install.sh</code> script uses bash shell syntax. If you get <tt>Bad substitution</tt> error, make sure you run script with bash.<br />
<br />
If you are on FreeBSD 12 or newer, the default linker is the <code>lld</code>. This may cause problems if you have code that uses, for example, the ''cthreads'' unit as in the following program:<br />
<br />
<syntaxhighlight lang="pascal"><br />
uses cthreads;<br />
begin<br />
writeln('hello'); <br />
end.<br />
</syntaxhighlight><br />
<br />
The executable generated for this program will cause a segmentation fault when run. To fix this issue, you need to install the GNU linker:<br />
<br />
<syntaxhighlight lang="bash"><br />
root# pkg install binutils<br />
</syntaxhighlight><br />
<br />
and add a symbolic-link from <code>/usr/local/bin/ld.bfd</code> to <code>/bin/ld.bfd</code>.<br />
<br />
== OpenBSD ==<br />
<br />
=== Installing from tar ===<br />
<br />
Select the appropriate 32 bit/64 bit tar file based on your system. For x86_64 64-bit OpenBSD, download from [https://sourceforge.net/projects/freepascal/files/OpenBSD/3.2.2/fpc-3.2.2.x86_64-openbsd.tar x86_64]. For i386 32-bit OpenBSD, download from [https://sourceforge.net/projects/freepascal/files/OpenBSD/3.2.2/fpc-3.2.2.i386-openbsd.tar i386].<br />
<br />
To install Free Pascal from a terminal:<br />
<br />
<syntaxhighlight lang="bash"><br />
$ wget https://sourceforge.net/projects/freepascal/files/OpenBSD/3.2.2/fpc-3.2.2.x86_64-openbsd.tar<br />
$ tar -xf fpc-3.2.2.x86_64-openbsd.tar<br />
$ cd fpc-3.2.2.x86_64-openbsd<br />
$ bash install.sh<br />
</syntaxhighlight><br />
<br />
Replace the desired Free Pascal version (3.0.4, 3.2.0, or 3.2.2) and architecture (x86_64 or i386) as required. <br />
<br />
If you want to install the Free Pascal Compiler globally, for example in <tt>/usr/local</tt>, run the install.sh script as root. <code>install.sh</code> script uses bash shell syntax. If you get <tt>Bad subtitution</tt> error, make sure you run script with bash.<br />
OpenBSD uses ksh as default shell. You need to install bash manually.<br />
<br />
== Installing from source on BSD/Linux ==<br />
<br />
Effectively, you need:<br />
<br />
1. A file with all FPC sources downloaded from git (https://gitlab.com/freepascal.org/fpc/source) or perhaps (https://sourceforge.net/projects/freepascal/files/Source/3.2.2/fpc-3.2.2.source.tar.gz).<br />
<br />
2. A starting (bootstrap) FPC compiler. An FPC release can always be built by the previously released FPC version, and FPC trunk can always be built by the current FPC release. You can download a bootstrap Free Pascal Compiler or use your distribution's package management/software system to install one.<br />
<br />
FPC build process:<br />
<br />
* Fetch necessary files (starting compiler), FPC source file or source svn directory<br />
* If using FPC source files: extract/de-tgz in work directory,<br />
* Build: enter work/fpc/ and run:<br />
<br />
<syntaxhighlight lang="bash"><br />
# Linux use: <br />
export MAKE=`which make` ; echo $MAKE <br />
# FreeBSD use (default csh, or tcsh):<br />
set MAKE=`which gmake` ; echo $MAKE<br />
# FreeBSD use (bash):<br />
export MAKE=`which gmake` ; echo $MAKE<br />
$MAKE all OPT='-gl' FPC=/path/to/startingcompiler-name-ppcx64<br />
# $MAKE is make on Linux and gmake on BSD <br />
# /path/to/ can be omitted when ppc386 (32 bit) or ppcx64 (64 bit) is in the path<br />
</syntaxhighlight><br />
<br />
* Install FPC. Again in work/fpc, run<br />
<br />
<syntaxhighlight lang="bash"><br />
$MAKE install FPC=compiler/ppcx64 PREFIX=$THEPREFIX<br />
#replace the FPC=compiler/ppcx64 (or ppc386 for 32 bit) with the relevant compiler if not on Intel x86<br />
#THEPREFIX= usually is /usr/local or just /usr, but eg on NetBSD it is /usr/pkg for ports)<br />
</syntaxhighlight><br />
<br />
* Create a symlink:<br />
<br />
<syntaxhighlight lang="bash"><br />
ln -s $THEPREFIX/lib/fpc/3.2.2/ppcx64 $THEPREFIX/bin/ppcx64<br />
</syntaxhighlight><br />
<br />
* Install sources:<br />
<br />
<syntaxhighlight lang="bash"><br />
$MAKE install sourceinstall PREFIX=$THEPREFIX<br />
</syntaxhighlight><br />
<br />
* Create a symlink for default FPC source path:<br />
<br />
<syntaxhighlight lang=bash><br />
ln -sf $THEPREFIX/share/src/3.2.2/fpc /usr/share/fpcsrc<br />
</syntaxhighlight><br />
<br />
* Set up fpc.cfg configuration file:<br />
<br />
<syntaxhighlight lang="bash"><br />
$THEPREFIX/lib/fpc/3.2.2/samplecfg $THEPREFIX/lib/fpc/3.2.0 $ETCDIR<br />
</syntaxhighlight><br />
<br />
* Optionally test to see if <code>ppcx64 -i</code> (or whatever compiler your architecture uses) gives output, else give a warning that user needs to add $PREFIX/bin to the current path. Try to compile a program with <code>ppcx64 -viwn</code>, and see if that gives errors.<br />
<br />
Notes:<br />
<br />
* If you need fpcmake package lists, you need to generate or supply them yourself, (in the port, or in an extra archive) either way, do a dummy install to /tmp/pack and determine the files installed with <syntaxhighlight lang="bash">find . >ll</syntaxhighlight><br />
<br />
* $THEPREFIX and $ETCDIR should be user configurable. Otherwise local installs aren't possible.<br />
<br />
* BSDHIER=1 on all make commands forces BSD hierarchy conventions.<br />
<br />
== Windows ==<br />
<br />
=== Installing an FPC release with the Windows installer ===<br />
<br />
{{Warning|If you are intending to also install Lazarus using the official Windows installer which installs both FPC + Lazarus, please ignore these instructions and instead refer to [[Installing Lazarus on Windows]] to avoid having to resolve any resulting conflicts.}}<br />
<br />
By far the easiest way to get a working installation of the Free Pascal Compiler is to download and run the appropriate self-extracting Windows installer from the official [https://sourceforge.net/projects/freepascal/files/Win32/3.2.2/ SourceForge repository for FPC 3.2.2] - this release contains the current release versions of the Free Pascal Compiler and the Free Pascal libraries. The Windows installer packages install very easily, and should work 'out-of-the-box'. You will be taken through a typical Windows installation wizard to install the FPC binaries and Free Pascal libraries:<br />
<br />
* Choose an installation location for FPC (suggested <tt>C:\FPC\3.2.2</tt>)<br />
* Choose installation type (recommended Full Installation)<br />
* Choose file associations, configuration file creation etc<br />
* Install<br />
* View README<br />
<br />
=== Installing from source ===<br />
<br />
Installing from source with a command line git client is not for novices, since you also need a working bootstrap compiler to be able to compile the Free Pascal Compiler from source. Instructions for obtaining a bootstrap compiler are included below.<br />
<br />
==== Obtaining the source with a command line git client ====<br />
<br />
The easiest way to get the Free Pascal Compiler source is by using a git client. The exact commands may vary between git clients; the commands given below are for the command line git client downloadable from [https://gitforwindows.org/ GitForWindows.org].<br />
<br />
===== Source for a release version of FPC =====<br />
<br />
First create a directory in which you'd like to keep the source. Any normal user can do this. Create a directory for FPC (eg <tt>C:\Source</tt>) by typing the following at a command prompt:<br />
<br />
<syntaxhighlight lang=dos><br />
C:\Users\you> cd \<br />
C:\> mkdir Source<br />
C:\> cd Source<br />
</syntaxhighlight><br />
<br />
Now type issue the following command at a command prompt:<br />
<br />
<syntaxhighlight lang="dos"><br />
C:\Source> git clone -b release_3_2_2 https://gitlab.com/freepascal.org/fpc/source.git fpc-3.2.2<br />
</syntaxhighlight><br />
<br />
Wait while the git repository is downloaded to the fpc-3.2.2 directory in your newly created Source directory. This can take a while... have a look at the helpful [https://forum.lazarus.freepascal.org/ Free Pascal Forums] while you wait :-) You may also like to read the excellent [[Forum|How to use the Forum]] Wiki article which explains how to get answers to pesky questions quickly. New Forum users are always welcome.<br />
<br />
===== Source for a non-release version of FPC =====<br />
<br />
There are two current non-release branches of the Free Pascal Compiler: the development (main) branch and the Fixes 3.2 branch which includes additional fixes to the released 3.2.2 version. Developers, and those who like living on the bleeding edge and testing new features and fixes, will choose the development version; more normal users, who wish to use a stable branch with some additional fixes since the latest release version, will choose the Fixes branch. The instructions below cover both these branches.<br />
<br />
If it does not already exist, create a directory in which you'd like to keep the FPC source (eg <tt>C:\Source</tt>). Any normal user can do this. Now type the following command at a command prompt:<br />
<br />
''For the Fixes branch of FPC 3.2''<br />
<br />
<syntaxhighlight lang="dos"><br />
C:\Source> git clone -b fixes_3_2 https://gitlab.com/freepascal.org/fpc/source.git fpc-fixes-3.2.0<br />
</syntaxhighlight><br />
<br />
''For the Development (main) branch of FPC''<br />
<br />
<syntaxhighlight lang="dos"><br />
C:\Source> git clone -b main https://gitlab.com/freepascal.org/fpc/source.git fpc-main<br />
</syntaxhighlight><br />
<br />
Hint: To update your local copy of the repository with subsequent changes, you need only do:<br />
<br />
''For the Fixes branch of FPC 3.2''<br />
<br />
<syntaxhighlight lang="dos"><br />
C:\> cd C:\Source\fpc-fixes-3.2<br />
C:\Source\fpc-fixes-3.2> git clean -f -d<br />
C:\Source\fpc-fixes-3.2> git pull<br />
</syntaxhighlight><br />
<br />
''For the Development (main) branch of FPC''<br />
<br />
<syntaxhighlight lang="dos"><br />
C:\> cd C:\Source\fpc-main<br />
C:\Source\fpc-trunk> git clean -f -d<br />
C:\Source\fpc-trunk> git pull<br />
</syntaxhighlight><br />
<br />
<!-- Needs further updating for git<br />
==== Obtaining the source with the GUI TortoiseGit client ====<br />
<br />
The first thing you will probably need to do is download the GUI TortoiseGit git client from TortoiseGit website [https://tortoisegit.org/download/ Download file area] if you do not already have it available. <br />
<br />
After downloading the appropriate Windows installer and installing it, you are ready to begin. You will notice that TortoiseGit has added a number of shortcuts to the File Explorer context menu so that you can work easily with svn without needing to use the command prompt. <br />
<br />
===== Source for a release version of FPC =====<br />
<br />
To download the latest release version Free Pascal Compiler source from the git repository, first create the directory in which you'd like to keep the source. Any normal user can do this:<br />
<br />
* Open File Explorer and navigate to "Local Disk C:"<br />
* Choose Home > New folder in File Explorer and create a directory for the FPC source (eg <tt>C:\Source</tt>).<br />
* Right click in File Explorer in your newly created directory and you should see an entry for "SVN Checkout..." in the context menu, choose this entry and you will be presented with a TortoiseSVN Checkout Dialog. <br />
* Fill in the "URL of the Repository" field with this URL: https://svn.freepascal.org/svn/fpc/tags/release_3_2_0<br />
* In the "Checkout directory" field add <tt>fpc-3.2.0</tt> to the existing C:\Source\</tt> in the field so you end up with <tt>C:\Source\fpc-3.2.0</tt>. This is the directory which will house your local copy of the source repository. <br />
* Leave the other fields at their default and click OK.<br />
* Now you will be presented with a new dialog which shows the progress of the checkout... wait while the Subversion repository is downloaded to the fpc-3.2.0 directory in your newly created Source directory. This can take a while... have a look at the helpful [https://forum.lazarus.freepascal.org/ Free Pascal Forums] while you wait :-) You may also like to read the excellent [[Forum|How to use the Forum]] Wiki article which explains how to get answers to pesky questions quickly. New Forum users are always welcome.<br />
<br />
===== Source for a non-release version of FPC =====<br />
<br />
There are two current non-release branches of the Free Pascal Compiler: the development (trunk) branch and the Fixes 3.2.0 branch which includes additional fixes to the released 3.2.0 version. Developers, and those who like living on the bleeding edge and testing new features and fixes, will choose the development version; more normal users, who wish to use a stable branch with some additional fixes since the latest release version, will choose the Fixes branch. The instructions below cover both these branches.<br />
<br />
The instructions are the same as for a [[#Source for a non-release version of FPC|release version]] of the Free Pascal Compiler given above, except that you will need to replace the "URL of the Repository" and "Checkout directory" fields with the appropriate values set out below:<br />
<br />
''For the Fixes branch of FPC''<br />
<br />
Repository URL: <nowiki>https://svn.freepascal.org/svn/fpc/branches/fixes_3_2_0</nowiki> <br />
Checkout Directory: C:\Source\fpc-fixes-3.2.0<br />
<br />
''For the Development (trunk) branch of FPC''<br />
<br />
Repository URL: <nowiki>https://svn.freepascal.org/svn/fpc/trunk </nowiki><br />
Checkout Directory: C:\Source\fpc-trunk<br />
<br />
''Updating your local repository''<br />
<br />
To '''update''' your local copy of the repository with subsequent source changes:<br />
<br />
* Open File Explorer, navigate to your <tt>C:\Source</tt> and right-click on the <tt>fpc-fixes-3.2.0</tt> or <tt>fpc-trunk</tt> directory.<br />
* You should see an entry for "TortoiseSVN", if you hover over it, another longer context menu will be presented from which you should choose "Clean up...". In the Cleanup dialog, check both "Delete unversioned files and folders" and "Delete ignored files and folders".<br />
* Now right-click on the <tt>fpc-fixes-3.2.0</tt> or <tt>fpc-trunk</tt> directory again and you should see an entry for "SVN Update" in the context menu, choose this entry and you will be presented with a TortoiseSVN Update Dialog and the update will start without you needing to do anything else. When the update finishes, click OK to exit.<br />
END OF NEEDS UPDATE HERE <br />
--><br />
==== Obtaining a bootstrap compiler ====<br />
<br />
To obtain a bootstrap Free Pascal Compiler, download the distribution package [https://www.freepascal.org/down/i386/win32.html fpc-3.2.2.i386-win32.exe] and run it - it is a self-extracting installer, so just follow the instructions to install it. The installer should set the PATH environment variable as appropriate.<br />
<br />
Then restart windows.<br />
<br />
==== Compiling the FPC source ====<br />
<br />
'''Before you start''' <br />
<br />
* The installation PREFIX in the instructions that follow is totally dependent on the directory in which you installed FPC. The Windows FPC installer uses a default location of <tt>C:\FPC</tt> and the FPC 3.2.2 release files are placed in <tt>C:\FPC\3.2.2</tt>. Since versions change relatively frequently, it is recommended that you just select and maintain a single PREFIX default location without any regard to FPC version numbers. A reasonable PREFIX to adopt is the one used by the Windows FPC installer (<tt>C:\FPC</tt>) but you must also make sure that the <tt>C:\FPC\bin\i386-win32\</tt> directory is added to your path environment variable.<br />
<br />
* Check that the <tt>make.exe</tt> which is found first in your path environment variable is the GNU one included with your bootstrap FPC compiler. If you have a recent version of Delphi installed, watch out for its version of <tt>make</tt> being earlier in your path. It does not understand the FPC Makefile and you will receive many errors if you try to use it. You can check this by opening a command prompt and typing <tt>make -v</tt>. The result should be (or very similar):<br />
<br />
[[file:fpc_windows_make.png]]<br />
<br />
* If you need to change your path so that the correct <tt>make.exe</tt> is found, you can either do it temporarily or permanently.<br />
** To change it temporarily (it is in effect only until you close the current command prompt) to find <tt>make.exe</tt> in <tt>C:\FPC\3.2.2\bin\i386-win32</tt>, at a command prompt type: <syntaxhighlight lang=dos><br />
C:\Source> set PATH=C:\FPC\3.2.2\bin\i386-win32;%PATH%<br />
</syntaxhighlight><br />
** To change it permanently, open Control Panel > System and choose "Advanced system settings" under "Related settings" towards the bottom of the page. Now choose "Environment Variables..." in the System Properties dialog. Here you will probably need to change the Path that appears under "System variables" so that <tt>C:\FPC\3.2.2\bin\i386-win32</tt> occurs first.<br />
<br />
'''Instructions'''<br />
<br />
* At a command prompt, navigate to your local FPC source directory (eg <tt>C:\Source\fpc-3.2.2</tt>) by typing:<br />
<br />
<syntaxhighlight lang=dos><br />
C:\> cd Source\fpc-3.2.2<br />
</syntaxhighlight><br />
<br />
* Compile the FPC source with:<br />
<br />
<syntaxhighlight lang=dos><br />
C:/Source/fpc-3.2.2> make distclean all<br />
</syntaxhighlight><br />
<br />
* To overwrite an existing FPC installation in, for example, <tt>C:\FPC</tt>) type: <br />
<br />
<syntaxhighlight lang=dos><br />
C:/Source/fpc-3.2.2> make install PREFIX=C:\FPC<br />
</syntaxhighlight><br />
<br />
* If you also need the cross-compiler to x86_64, type the following commands which will compile the cross-compiler and then install it:<br />
<br />
<syntaxhighlight lang="dos"><br />
C:/Source/fpc-3.2.2> make all OS_TARGET=win64 CPU_TARGET=x86_64 INSTALL_PREFIX=C:\FPC\3.2.2 PP=C:\FPC\3.2.2\bin\i386-win32\ppc386.exe DATA2INC=C:\FPC\3.2.2\bin\i386-win32\data2inc.exe<br />
<br />
C:/Source/fpc-3.2.2> make crossinstall OS_TARGET=win64 CPU_TARGET=x86_64 INSTALL_PREFIX=C:\FPC\3.2.2 PP=C:\FPC\3.2.2\bin\i386-win32\ppc386.exe DATA2INC=C:\FPC\3.2.2\bin\i386\win32\data2inc.exe<br />
</syntaxhighlight><br />
<br />
The cross-compiler to compile programs for Windows 64 bit may now be found here: <tt>C:\FPC\3.2.2\bin\i386-win32\ppcrossx64</tt>.<br />
<br />
Done!<br />
<br />
== macOS ==<br />
<br />
{{Warning|If you are intending to also install Lazarus using the official macOS installation packages which install both FPC + Lazarus, please ignore these instructions and instead refer to [[Installing Lazarus on macOS]] to avoid having to resolve any resulting FPC conflicts.}}<br />
<br />
=== Installing an FPC release from the official macOS packages ===<br />
<br />
==== Step 1: Download Xcode (optional) ====<br />
<br />
Xcode is a 12GB download which will take up 16GB of disk space. You '''only''' need to download and install the full Xcode development environment '''if''' you need:<br />
<br />
* the SDKs for '''iOS''', '''iPadOS''', watchOS and tvOS; or<br />
* to validate and upload apps to the Mac App Store; or<br />
* to [[Notarization for macOS 10.14.5+|notarise]] apps for distribution outside of the Mac App Store. <br />
<br />
Xcode 11.3.1 for use on macOS 10.14 Mojave must now be installed by downloading it from [http://developer.apple.com/ Apple Developer Connection] (ADC), which requires free registration. Xcode 11.4.x for use on macOS 10.15 Catalina can be installed from the [https://apps.apple.com/us/genre/mac-developer-tools/id12002?mt=12 Mac App store]. Note that you must first move any old Xcode versions from the Applications folder into the trash or rename the Xcode app (eg Xcode.app to Xcode_1014.app). You can select which version of Xcode to use with the command line utility <tt>xcode-select</tt>.<br />
<br />
Older systems:<br />
<br />
The developer tools can be installed from the original macOS installation disks or a newer copy downloaded from the [http://developer.apple.com/ Apple Developer Connection] (ADC), which requires free registration. Download the Xcode file, it will end up in your Downloads directory as a zip file. Click it. It is unarchived into your Downloads directory. You may be happy with it there but maybe not. Other users will see the path to it but be unable to use it. And it is untidy there. So I moved mine and then told xcode-select where it was moved to (in a terminal) -<br />
<br />
<syntaxhighlight lang="bash"><br />
mv Downloads/Xcode.app /Developer/.<br />
sudo xcode-select -s /Developer/Xcode.app/Contents/Developer <br />
</syntaxhighlight><br />
<br />
==== Step 2: Xcode Command Line Tools ====<br />
<br />
This is shown here as a separate step because it really is a separate step in addition to Step 1. Don't confuse this with the internal Xcode command line tools that the Xcode GUI will tell you are already installed. FPC does not use those Xcode internal command line tools, so do the following (it is quick and easy)-<br />
<br />
<syntaxhighlight lang="bash"><br />
sudo xcode-select --install<br />
sudo xcodebuild -license accept<br />
</syntaxhighlight><br />
<br />
You only need to enter the first of the two commands above unless you have also installed the full Xcode package. If you have only installed the command line tools, you should omit entering the ''xcodebuild'' command.<br />
<br />
==== Step 3: Download FPC ====<br />
<br />
Download the Free Pascal Compiler (FPC) and FPC source from the [https://www.freepascal.org/download.html Free Pascal website]. When you arrive at the download page, select a mirror and then choose the correct version for your operating system. <br />
<br />
These installation packages are built by the FPC developers and track formal releases. As these installation packages are not code signed nor notarized by Apple, you need to either control-click on the installation package and choose "Open" or right-click on the installation package and choose "Open" in the contextual menu and confirm you want to install from an Unknown Developer.<br />
<br />
{{macOS FPC Source Installation}}<br />
<br />
=== Other installation options ===<br />
<br />
See [[Other_macOS_installation_options#Other_FPC_Installation_Options|Other FPC Installation Options]].<br />
<br />
== Testing the FPC Install ==<br />
<br />
You might like to try a simple and quick test of FPC at this stage. From the command line (Mac - Open an Application > Utilities > Terminal) start an editor (e.g. pico or nano), paste this very short program and save it as the file <tt>helloworld.pas</tt> (using the WriteOut command in GNU nano):<br />
<br />
<syntaxhighlight lang="pascal"><br />
program helloworld;<br />
begin<br />
writeln('hello world !');<br />
end.<br />
</syntaxhighlight><br />
<br />
Now exit the editor and compile this simple code by typing this command, before pressing {{keypress|Enter}}:<br />
<br />
<syntaxhighlight lang="bash"><br />
fpc helloworld.pas<br />
</syntaxhighlight><br />
<br />
It should very quickly make an executable called, you guessed it, "helloworld". Run this executable by typing this command and then pressing {{keypress|Enter}}:<br />
<br />
<syntaxhighlight lang="bash"><br />
./helloworld<br />
</syntaxhighlight><br />
<br />
If that worked, well done!<br />
<br />
== Useful command-line switches for compiling from source ==<br />
<br />
* Significantly speed up the FPC source tree compilation <br />
** <code>FPMAKEOPT="-T x"</code> (x = the number of CPU cores you have)<br />
<br />
* Reduce the number of times the compiler itself gets recompiled<br />
** <code>NOWPOCYCLE=1</code><br />
<br />
* Add debug information and disable optimizations during compilation of the compiler/RTL<br />
** <code>OPT="-gl -O-"</code><br />
<br />
== Installing cross compilers ==<br />
<br />
A cross compiler allows you to create binaries (executables) for a platform different from the platform being used for compilation. For example, working under macOS and creating executables for Win64 or Win32 (only up to and including macOS 10.14.6 due to Apple's removal of 32 bit frameworks). For details on how to do this, see [[Cross_compiling|Cross Compiling]].<br />
<br />
== See also ==<br />
<br />
* [[FPC|FPC Wiki Documentation]].<br />
* [https://www.freepascal.org/docs.html FPC Official Documentation].<br />
* [[FPC_New_Features_3.2.2|FPC 3.2.2 New Features]].<br />
* [[User_Changes_3.2.2|FPC 3.2.2 User Changes]] - may break existing code.<br />
* [[FPC_New_Features_Trunk|FPC New Features in development version]].<br />
* [[User Changes Trunk|FPC User Changes in development version]] - may break existing code.<br />
<br />
[[Category:FPC]]<br />
[[Category:Install]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Synapse&diff=158252Synapse2024-02-20T01:33:16Z<p>Dbannon: on line help</p>
<hr />
<div>{{Synapse}}<br />
<br />
Synapse provides an easy to use serial port and synchronous TCP/IP library.<br />
<br />
__TOC__<br />
<br />
== Overview ==<br />
<br />
Synapse offers [[Hardware_Access#Synaser|serial]] port and TCP/IP connectivity. It differs from other libraries that you only require to add some Synapse Pascal source code files to your code; no need for installing packages etc. The only exception is that you will need an external crypto library if you want to use encryption such as SSL/TLS/SSH. <br />
<br />
See the documentation on the official site (link below) for more details.<br />
<br />
== Installation ==<br />
<br />
Installation can be as simple as simply copying over all files to your application directory and adding the relevant Synapse units to your ''uses'' clause.<br />
<br />
A more elegant and recommended way is compiling the laz_synapse.lpk package so you can use the same units in all your projects.<br />
<br />
Synapse download / Wiki info page: [https://github.com/geby/synapse Download and Wiki]<br />
<br />
== Support and bug reporting ==<br />
<br />
The Synapse project has a mailing list where support is given and patches can be submitted.<br />
<br />
Bug reports can also be mailed to the mailing list.<br />
<br />
See the [http://www.ararat.cz/synapse/doku.php/support Synapse support page] and some well presented [http://synapse.ararat.cz/doc/help/ on line help]<br />
<br />
== SSL/TLS support ==<br />
<br />
You can use OpenSSL, CryptLib, StreamSecII or OpenStreamSecII SSL support with Synapse. By default, no SSL support is used. <br />
<br />
The support is activated by putting the chosen unit name in the uses section in your project. You also have to put the binary library file in your project path (Windows), or install it into your library search path (Linux, macOS, FreeBSD). <br />
<br />
Synapse loads SSL library files in runtime as dynamic libraries.<br />
<br />
* For detailed information refer to [http://www.ararat.cz/synapse/doku.php/public:howto:sslplugin SSL/TLS Plugin Architecture]<br />
* Some crypt libraries can be obtained from: http://synapse.ararat.cz/files/crypt/<br />
<br />
=== Missing library ===<br />
<br />
On Linux you need to make sure the required dynamic library is present/installed on your system. In case of cryptlib if the library is not present on the system, an error message appears during linking:<br />
<br />
/usr/bin/ld: cannot find -lcl<br />
<br />
A similar message will be displayed when using other dynamic libraries.<br />
<br />
== Web server example ==<br />
<br />
See the [[Networking#Webserver example|Webserver example]].<br />
<br />
== QOTD server query example ==<br />
<br />
See the [[QOTD|Quote of the Day server query example]]. <br />
<br />
== Sending email ==<br />
<br />
Refer to the [[Synapse - Email Examples]] article.<br />
<br />
== Downloading files ==<br />
<br />
=== From an FTP server ===<br />
<br />
Given an URL and a (path and) file name, this will download it from an FTP server.<br />
It's mostly a wrapper around the Synapse code meant to make downloading easier when handling arbitrary files.<br />
If you know exactly what you're going to download where, just a call to Synapse:<br />
<br />
<syntaxhighlight lang="pascal"><br />
FtpGetFile<br />
</syntaxhighlight><br />
<br />
will get you very far.<br />
<br />
<syntaxhighlight lang="pascal"><br />
function DownloadFTP(URL, TargetFile: string): boolean;<br />
const<br />
FTPPort=21;<br />
FTPScheme='ftp://'; //URI scheme name for FTP URLs<br />
var<br />
Host: string;<br />
Port: integer;<br />
Source: string;<br />
FoundPos: integer;<br />
begin<br />
// Strip out scheme info:<br />
if LeftStr(URL, length(FTPScheme))=FTPScheme then URL:=Copy(URL, length(FTPScheme)+1, length(URL));<br />
<br />
// Crude parsing; could have used URI parsing code in FPC packages...<br />
FoundPos:=pos('/', URL);<br />
Host:=LeftStr(URL, FoundPos-1);<br />
Source:=Copy(URL, FoundPos+1, Length(URL));<br />
<br />
//Check for port numbers:<br />
FoundPos:=pos(':', Host);<br />
Port:=FTPPort;<br />
if FoundPos>0 then<br />
begin<br />
Host:=LeftStr(Host, FoundPos-1);<br />
Port:=StrToIntDef(Copy(Host, FoundPos+1, Length(Host)),21);<br />
end;<br />
Result:=FtpGetFile(Host, IntToStr(Port), Source, TargetFile, 'anonymous', 'fpc@example.com');<br />
if result=false then writeln('DownloadFTP: error downloading '+URL+'. Details: host: '+Host+'; port: '+Inttostr(Port)+'; remote path: '+Source+' to '+TargetFile);<br />
end;<br />
</syntaxhighlight><br />
<br />
Example to get list of files in given path<br />
<br />
<syntaxhighlight lang="pascal"><br />
//Use ftpsend unit<br />
<br />
function FtpGetDir(const IP, Port, Path, User, Pass: string; DirList: TStringList): Boolean;<br />
var<br />
i: Integer;<br />
s: string;<br />
begin<br />
Result := False;<br />
with TFTPSend.Create do<br />
try<br />
Username := User;<br />
Password := Pass;<br />
TargetHost := IP;<br />
TargetPort := Port;<br />
if not Login then<br />
Exit;<br />
Result := List(Path, False);<br />
for i := 0 to FtpList.Count -1 do<br />
begin<br />
s := FTPList[i].FileName;<br />
DirList.Add(s);<br />
end;<br />
Logout;<br />
finally<br />
Free;<br />
end;<br />
end; <br />
</syntaxhighlight><br />
<br />
=== From an HTTP server ===<br />
<br />
Given an URL and a (path and) file name, this will download it from an HTTP server.<br />
Note that this code checks the HTTP status code (like 200, 404) to see if the document we got back from the server is the desired file or an error page.<br />
<br />
==== Simple version ====<br />
<br />
<syntaxhighlight lang="pascal"><br />
...<br />
uses httpsend,<br />
...<br />
function DownloadHTTP(URL, TargetFile: string): Boolean;<br />
var<br />
HTTPGetResult: Boolean;<br />
HTTPSender: THTTPSend;<br />
begin<br />
Result := False;<br />
HTTPSender := THTTPSend.Create;<br />
try<br />
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);<br />
if (HTTPSender.ResultCode >= 100) and (HTTPSender.ResultCode<=299) then begin<br />
HTTPSender.Document.SaveToFile(TargetFile);<br />
Result := True;<br />
end; <br />
finally<br />
HTTPSender.Free;<br />
end;<br />
end;</syntaxhighlight><br />
<br />
==== Advanced version ====<br />
<br />
<syntaxhighlight lang="pascal"><br />
...<br />
uses httpsend<br />
...<br />
function DownloadHTTP(URL, TargetFile: string): Boolean;<br />
// Download file; retry if necessary.<br />
// Could use Synapse HttpGetBinary, but that doesn't deal<br />
// with result codes (i.e. it happily downloads a 404 error document)<br />
const<br />
MaxRetries = 3;<br />
var<br />
HTTPGetResult: Boolean;<br />
HTTPSender: THTTPSend;<br />
RetryAttempt: Integer;<br />
begin<br />
Result := False;<br />
RetryAttempt := 1;<br />
HTTPSender := THTTPSend.Create;<br />
try<br />
try<br />
// Try to get the file<br />
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);<br />
while (HTTPGetResult = False) and (RetryAttempt < MaxRetries) do<br />
begin<br />
Sleep(500 * RetryAttempt);<br />
HTTPSender.Clear;<br />
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);<br />
RetryAttempt := RetryAttempt + 1;<br />
end;<br />
// If we have an answer from the server, check if the file<br />
// was sent to us.<br />
case HTTPSender.Resultcode of<br />
100..299:<br />
begin<br />
HTTPSender.Document.SaveToFile(TargetFile);<br />
Result := True;<br />
end; //informational, success<br />
300..399: Result := False; // redirection. Not implemented, but could be.<br />
400..499: Result := False; // client error; 404 not found etc<br />
500..599: Result := False; // internal server error<br />
else Result := False; // unknown code<br />
end;<br />
except<br />
// We don't care for the reason for this error; the download failed.<br />
Result := False;<br />
end;<br />
finally<br />
HTTPSender.Free;<br />
end;<br />
end;<br />
</syntaxhighlight><br />
<br />
==== Simple version with progress ====<br />
<br />
The following example shows how to get progress information from the HTTP download, as well as the file size.<br />
The file size is retrieved from the header information. <br />
<br />
<syntaxhighlight lang="pascal"><br />
unit uhttpdownloader;<br />
<br />
// Essential to change this. The default is {$mode objfpc}{$H+} and does not work.<br />
{$mode Delphi}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, httpsend, blcksock, typinfo;<br />
<br />
type<br />
IProgress = interface<br />
procedure ProgressNotification(Text: String; CurrentProgress : integer; MaxProgress : integer);<br />
end;<br />
<br />
type<br />
{ THttpDownloader }<br />
<br />
THttpDownloader = class<br />
public<br />
function DownloadHTTP(URL, TargetFile: string; ProgressMonitor : IProgress): Boolean;<br />
private<br />
Bytes : Integer;<br />
MaxBytes : Integer;<br />
HTTPSender: THTTPSend;<br />
ProgressMonitor : IProgress;<br />
procedure Status(Sender: TObject; Reason: THookSocketReason; const Value: String);<br />
function GetSizeFromHeader(Header: String):integer;<br />
end;<br />
<br />
implementation<br />
<br />
function THttpDownloader.DownloadHTTP(URL, TargetFile: string; ProgressMonitor : IProgress): Boolean;<br />
var<br />
HTTPGetResult: Boolean;<br />
begin<br />
Result := False;<br />
Bytes:= 0;<br />
MaxBytes:= -1;<br />
Self.ProgressMonitor:= ProgressMonitor;<br />
<br />
HTTPSender := THTTPSend.Create;<br />
try<br />
HTTPSender.Sock.OnStatus:= Status;<br />
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);<br />
if (HTTPSender.ResultCode >= 100) and (HTTPSender.ResultCode<=299) then begin<br />
HTTPSender.Document.SaveToFile(TargetFile);<br />
Result := True;<br />
end;<br />
finally<br />
HTTPSender.Free;<br />
end;<br />
end;<br />
<br />
procedure THttpDownloader.Status(Sender: TObject; Reason: THookSocketReason; const Value: String);<br />
var<br />
V, currentHeader: String;<br />
i: integer;<br />
begin<br />
//try to get filesize from headers<br />
if (MaxBytes = -1) then<br />
begin<br />
for i:= 0 to HTTPSender.Headers.Count - 1 do<br />
begin<br />
currentHeader:= HTTPSender.Headers[i];<br />
MaxBytes:= GetSizeFromHeader(currentHeader);<br />
if MaxBytes <> -1 then break;<br />
end;<br />
end;<br />
<br />
V := GetEnumName(TypeInfo(THookSocketReason), Integer(Reason)) + ' ' + Value;<br />
<br />
if Reason = THookSocketReason.HR_ReadCount then<br />
begin<br />
Bytes:= Bytes + StrToInt(Value);<br />
ProgressMonitor.ProgressNotification(V, Bytes, MaxBytes);<br />
end;<br />
end;<br />
<br />
function THttpDownloader.GetSizeFromHeader(Header: String): integer;<br />
var<br />
item : TStringList;<br />
begin<br />
Result:= -1;<br />
<br />
if Pos('Content-Length:', Header) <> 0 then<br />
begin<br />
item:= TStringList.Create();<br />
item.Delimiter:= ':';<br />
item.StrictDelimiter:=true;<br />
item.DelimitedText:=Header;<br />
if item.Count = 2 then<br />
begin<br />
Result:= StrToInt(Trim(item[1]));<br />
end;<br />
end;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
What are we doing here?<br />
<br />
First of all we look into the headers to get the file size. We have to wait and check if the header is there. The first events do not contain the Content-Length: information.<br />
<br />
Once found, we extract that information. There are several events popping up here, which you can react to. But we only check for THookSocketReason.HR_ReadCount in that example.<br />
<br />
"HR_ReadCount" provides us with the information how many bytes where read since the last event.<br />
<br />
The progress is then reported to the UI:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.ProgressNotification(Text: String; CurrentProgress: integer; MaxProgress: integer);<br />
begin<br />
if (MaxProgress <> -1) then<br />
begin<br />
ProgressBar.Max:= MaxProgress;<br />
end;<br />
<br />
ProgressBar.Position:= CurrentProgress;<br />
memoStatus.Lines.Add(Text);<br />
Application.ProcessMessages;<br />
end;<br />
</syntaxhighlight><br />
<br />
So, the final main unit will be:<br />
<br />
<syntaxhighlight lang="pascal"><br />
unit uMain;<br />
<br />
{$mode Delphi}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls, httpsend, blcksock, typinfo,<br />
uhttpdownloader;<br />
<br />
type<br />
<br />
{ TMainForm }<br />
<br />
TMainForm = class(TForm, IProgress)<br />
btnStartDownload: TButton;<br />
edtUrl: TEdit;<br />
labelUrl: TLabel;<br />
memoStatus: TMemo;<br />
ProgressBar: TProgressBar;<br />
SaveDialog: TSaveDialog;<br />
procedure btnStartDownloadClick(Sender: TObject);<br />
private<br />
{ private declarations }<br />
function GetFileNameFromURL(url: String):string;<br />
public<br />
{ public declarations }<br />
procedure ProgressNotification(Text: String; CurrentProgress : integer; MaxProgress : integer);<br />
end;<br />
<br />
var<br />
MainForm: TMainForm;<br />
<br />
implementation<br />
<br />
{$R *.lfm}<br />
<br />
{ TMainForm }<br />
<br />
procedure TMainForm.btnStartDownloadClick(Sender: TObject);<br />
var<br />
fileName: String;<br />
downloader: THttpDownloader;<br />
success: boolean;<br />
begin<br />
fileName:= GetFileNameFromURL(edtUrl.Text);<br />
SaveDialog.FileName:=fileName;<br />
if (SaveDialog.Execute) then<br />
begin<br />
memoStatus.Lines.Clear;<br />
ProgressBar.Position:=0;<br />
downloader:= THttpDownloader.Create();<br />
success:= downloader.DownloadHTTP(edtUrl.Text, SaveDialog.FileName, Self);<br />
<br />
ProgressBar.Position:=0;<br />
if Success then<br />
memoStatus.Lines.Add('Download successful')<br />
else<br />
memoStatus.Lines.Add('Error during download');<br />
<br />
end;<br />
end;<br />
<br />
function TMainForm.GetFileNameFromURL(url: String): string;<br />
var i, l : integer;<br />
fileName, current : String;<br />
begin<br />
fileName:= '';<br />
l:= Length(url);<br />
for i:= l downto 0 do begin<br />
current:= url[i];<br />
if current <> '/' then<br />
begin<br />
fileName:= current + fileName;<br />
end else begin<br />
Result:= fileName;<br />
break;<br />
end;<br />
end;<br />
end;<br />
<br />
procedure TMainForm.ProgressNotification(Text: String; CurrentProgress: integer; MaxProgress: integer);<br />
begin<br />
if (MaxProgress <> -1) then ProgressBar.Max:= MaxProgress;<br />
ProgressBar.Position:= CurrentProgress;<br />
memoStatus.Lines.Add(Text);<br />
Application.ProcessMessages;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
Reference: https://andydunkel.net/2015/09/09/lazarus_synapse_progress/<br />
<br />
==== From an HTTP server by parsing URLs: Sourceforge ====<br />
<br />
Please see [[Download from SourceForge]] for an example of downloading from sourceforge.net.<br />
<br />
=== From an HTTPS server ===<br />
<br />
This is similar to downloading from an HTTP server. In addition you need to [[Synapse#SSL.2FTLS_support|activate SSL/TLS support]] and obtain the binary file(s) for the needed library. Then you can use the same DownloadHTTP function for downloading a file from a URL starting with '''https://'''.<br />
<br />
== SSH/Telnet client sample program ==<br />
<br />
Below you will find a unit that allows you to use telnet/SSH client functionality that uses the synapse tlntsend.pas unit. An example program shows how to use this.<br />
A different, simpler way is illustrated by Leonardo Ramé at [http://leonardorame.blogspot.com/2010/01/synapse-based-ssh-client.html]. His example cannot use telnet and only sends one command, though.<br />
<br />
=== Requirements ===<br />
<br />
Apart from the Synapse sources (of which you only need a couple), if you want to use SSH functionality, you will need an encryption library that Synapse uses. If you only use Telnet, you don't need this.<br />
<br />
There are 2 choices:<br />
* Cryptlib library. Advantage: stable. Apparently able to use private keys but these are in some format that is not widely supported.<br />
* LibSSH2 library. Pascal bindings still in development, but you can use a file with your private key (in OpenSSH format) to authenticate.<br />
<br />
==== Cryptlib ====<br />
<br />
* On Windows, download a binary version of the cryptlib DLL (CL32.DLL) and put it in your source directory. If you compile to a different directory or distribute your program, you will need to distribute the DLL as well.<br />
* On Linux and OSX, install cryptlib via your package manager/other means. When distributing your application, mark cryptlib as a requirement in your .deb/.rpm/whatever package.<br />
<br />
You will also need the bindings (cryptlib.pas), present in the source distribution of cryptlib.<br />
<br />
The versions of the cryptlib binary and the bindings must match.<br />
<br />
{{Note|It seems that cryptlib is not suitable to connect to linux machines, though AIX works. Use SSH2 instead.}}<br />
<br />
==== LibSSH2 ====<br />
<br />
* On Windows, download a binary version of the libssh2 DLL (LIBSSH2.DLL) and put it in your source directory. If you compile to a different directory or distribute your program, you will need to distribute the DLL as well.<br />
* On Linux and macOS, install libssh2 via your package manager/other means. When distributing your application:<br />
** Linux: mark libssh2 as a requirement in your .deb/.rpm/whatever package.<br />
** macOS: include libssh2 in your [[Application Bundle]]'s Resources directory.<br />
<br />
You will also need ssl_libssh2.pas (see below) and the bindings: (libssh2.pas, see [http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465 this forum post]). The libssh2 binary and the bindings must match.<br />
<br />
=== Synapse libssh2 SSL plugin ===<br />
<br />
{{Note| plugin is not completed.}}<br />
<br />
<syntaxhighlight lang="pascal"><br />
{<br />
ssl_libssh2.pas version 0.2<br />
<br />
SSH2 support (draft) plugin for Synapse Library (http://www.ararat.cz/synapse) by LibSSH2 (http://libssh2.org)<br />
Requires: libssh2 pascal interface - http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465 and<br />
libssh2.dll with OpenSSL.<br />
<br />
(С) Alexey Suhinin http://x-alexey.narod.ru<br />
}<br />
<br />
{$IFDEF FPC}<br />
{$MODE DELPHI}<br />
{$ENDIF}<br />
{$H+}<br />
<br />
unit ssl_libssh2;<br />
<br />
interface<br />
<br />
uses<br />
SysUtils,<br />
blcksock, synsock,<br />
libssh2;<br />
<br />
type<br />
{:@abstract(class implementing CryptLib SSL/SSH plugin.)<br />
Instance of this class will be created for each @link(TTCPBlockSocket).<br />
You not need to create instance of this class, all is done by Synapse itself!}<br />
TSSLLibSSH2 = class(TCustomSSL)<br />
protected<br />
FSession: PLIBSSH2_SESSION;<br />
FChannel: PLIBSSH2_CHANNEL;<br />
function SSHCheck(Value: integer): Boolean;<br />
function DeInit: Boolean;<br />
public<br />
{:See @inherited}<br />
constructor Create(const Value: TTCPBlockSocket); override;<br />
destructor Destroy; override;<br />
function Connect: boolean; override;<br />
function LibName: String; override;<br />
function Shutdown: boolean; override;<br />
{:See @inherited}<br />
function BiShutdown: boolean; override;<br />
{:See @inherited}<br />
function SendBuffer(Buffer: TMemory; Len: Integer): Integer; override;<br />
{:See @inherited}<br />
function RecvBuffer(Buffer: TMemory; Len: Integer): Integer; override;<br />
{:See @inherited}<br />
function WaitingData: Integer; override;<br />
{:See @inherited}<br />
function GetSSLVersion: string; override;<br />
published<br />
end;<br />
<br />
implementation<br />
<br />
{==============================================================================}<br />
function TSSLLibSSH2.SSHCheck(Value: integer): Boolean;<br />
var<br />
PLastError: PAnsiChar;<br />
ErrMsgLen: Integer;<br />
begin<br />
Result := true;<br />
FLastError := 0;<br />
FLastErrorDesc := '';<br />
if Value<0 then<br />
begin<br />
FLastError := libssh2_session_last_error(FSession, PLastError, ErrMsglen, 0);<br />
FLastErrorDesc := PLastError;<br />
Result := false;<br />
end;<br />
end;<br />
<br />
<br />
function TSSLLibSSH2.DeInit: Boolean;<br />
begin<br />
if Assigned(FChannel) then<br />
begin<br />
libssh2_channel_free(FChannel);<br />
FChannel := nil;<br />
end;<br />
if Assigned(FSession) then<br />
begin<br />
libssh2_session_disconnect(FSession,'Goodbye');<br />
libssh2_session_free(FSession);<br />
FSession := nil;<br />
end;<br />
FSSLEnabled := False;<br />
Result := true;<br />
end;<br />
<br />
constructor TSSLLibSSH2.Create(const Value: TTCPBlockSocket);<br />
begin<br />
inherited Create(Value);<br />
FSession := nil;<br />
FChannel := nil;<br />
end;<br />
<br />
destructor TSSLLibSSH2.Destroy;<br />
begin<br />
DeInit;<br />
inherited Destroy;<br />
end;<br />
<br />
function TSSLLibSSH2.Connect: boolean;<br />
begin<br />
Result := False;<br />
if SSLEnabled then DeInit;<br />
if (FSocket.Socket <> INVALID_SOCKET) and (FSocket.SSL.SSLType = LT_SSHv2) then<br />
begin<br />
FSession := libssh2_session_init();<br />
if not Assigned(FSession) then<br />
begin<br />
FLastError := -999;<br />
FLastErrorDesc := 'Cannot initialize SSH session';<br />
exit;<br />
end;<br />
if not SSHCheck(libssh2_session_startup(FSession, FSocket.Socket)) then<br />
exit;<br />
if (FSocket.SSL.PrivateKeyFile<>'') then<br />
begin<br />
if (not SSHCheck(libssh2_userauth_publickey_fromfile(FSession, PChar(FSocket.SSL.Username), nil, PChar(FSocket.SSL.PrivateKeyFile), PChar(FSocket.SSL.KeyPassword)))) then<br />
exit;<br />
end<br />
else<br />
if (FSocket.SSL.Username<>'') and (FSocket.SSL.Password<>'') then<br />
begin<br />
if (not SSHCheck(libssh2_userauth_password(FSession, PChar(FSocket.SSL.Username), PChar(FSocket.SSL.Password)))) then<br />
exit;<br />
end;<br />
FChannel := libssh2_channel_open_session(FSession);<br />
if not assigned(FChannel) then<br />
begin<br />
SSHCheck(-1); // get error<br />
if FLastError = 0 then<br />
begin<br />
FLastError := -999; // unknown error<br />
FLastErrorDesc := 'Cannot open session';<br />
end;<br />
exit;<br />
end;<br />
if not SSHCheck(libssh2_channel_request_pty(FChannel, 'vanilla')) then<br />
exit;<br />
if not SSHCheck(libssh2_channel_shell(FChannel)) then<br />
exit;<br />
FSSLEnabled := True;<br />
Result := True;<br />
end;<br />
end;<br />
<br />
function TSSLLibSSH2.LibName: String;<br />
begin<br />
Result := 'ssl_libssh2';<br />
end;<br />
<br />
function TSSLLibSSH2.Shutdown: boolean;<br />
begin<br />
Result := DeInit;<br />
end;<br />
<br />
<br />
function TSSLLibSSH2.BiShutdown: boolean;<br />
begin<br />
Result := DeInit;<br />
end;<br />
<br />
function TSSLLibSSH2.SendBuffer(Buffer: TMemory; Len: Integer): Integer;<br />
begin<br />
Result:=libssh2_channel_write(FChannel, PChar(Buffer), Len);<br />
SSHCheck(Result);<br />
end;<br />
<br />
function TSSLLibSSH2.RecvBuffer(Buffer: TMemory; Len: Integer): Integer;<br />
begin<br />
result:=libssh2_channel_read(FChannel, PChar(Buffer), Len);<br />
SSHCheck(Result);<br />
end;<br />
<br />
function TSSLLibSSH2.WaitingData: Integer;<br />
begin<br />
if libssh2_poll_channel_read(FChannel, Result) <> 1 then Result := 0;<br />
end;<br />
<br />
function TSSLLibSSH2.GetSSLVersion: string;<br />
begin<br />
Result:=libssh2_version(0);<br />
end;<br />
<br />
initialization<br />
if libssh2_init(0)=0 then<br />
SSLImplementation := TSSLLibSSH2;<br />
<br />
finalization<br />
libssh2_exit;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
=== Terminal client class ===<br />
<br />
The telnetsshclient.pas unit below wraps around the Synapse tlntsend.pas unit and abstracts logging in, sending commands and receiving output and logging out.<br />
<br />
If you only need a telnet client and can live without SSH support, comment out {$DEFINE HAS_SSH_SUPPORT} below so you don't need to have the libssh2 dll.<br />
<br />
This unit has been lightly tested on a Linux ssh/telnet server. Additional tests welcome.<br />
<br />
<syntaxhighlight lang="pascal"><br />
unit telnetsshclient;<br />
<br />
{ Wrapper around Synapse libraries and SSL library (libssh2+libssl<br />
is used right now)<br />
Download compiled Windows dll from e.g.<br />
http://alxdm.dyndns-at-work.com:808/files/windll_libssh2.zip<br />
Download FreePascal interface files:<br />
http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465<br />
<br />
This unit allows the user to send Telnet or SSH commands and get the output<br />
Thanks to Leonardo Rame<br />
http://leonardorame.blogspot.com/2010/01/synapse-based-ssh-client.html<br />
and Ludo Brands.<br />
<br />
Written by Reinier Olislagers 2011.<br />
Modified for libssh2 by Alexey Suhinin 2012.<br />
<br />
License of code:<br />
* MIT<br />
* LGPLv2 or later (with FreePascal static linking exception)<br />
* GPLv2 or later<br />
according to your choice.<br />
Free use allowed but please don't sue or blame me.<br />
<br />
Uses other libraries/components; different licenses may apply that also can influence the combined/compiled work.<br />
}<br />
<br />
{$mode objfpc}{$H+}<br />
{$DEFINE HAS_SSH_SUPPORT} //comment out if only telnet support required<br />
{$DEFINE LIBSSH2}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils,<br />
tlntsend<br />
{$IFDEF HAS_SSH_SUPPORT}<br />
{ssl - or actually ssh - libs required by tlntsend}<br />
{$IFDEF LIBSSH2}<br />
ssl_libssh2<br />
{$ELSE}<br />
ssl_cryptlib<br />
{$ENDIF}<br />
{$ENDIF HAS_SSH_SUPPORT} ;<br />
<br />
type<br />
TProtocolType = (Telnet, SSH); //Different means of connecting<br />
TServerType = (Unix, Windows); //line endings, mostly<br />
{ TelnetSSHClient }<br />
<br />
{ TTelnetSSHClient }<br />
<br />
TTelnetSSHClient = class(TTelnetSend)<br />
protected<br />
FConnected: boolean;<br />
FOutputPosition: integer; //Keeps track of position in output stream<br />
FProtocolType: TProtocolType;<br />
FServerLineEnding: string; //depends on FServerType<br />
FServerType: TServerType;<br />
FWelcomeMessage, FTelnetLoginPrompt, FTelnetPasswordPrompt: string;<br />
procedure SetPrivateKeyFile(Value: string);<br />
function GetPrivateKeyFile: string;<br />
{ Based on protocol and servertype, set expected serverside line ending}<br />
procedure DetermineLineEnding;<br />
{ Sets port if no explicit port set. Uses protocol type: SSH or telnet}<br />
procedure DeterminePort;<br />
function GetSessionLog: string;<br />
procedure ProtocolTypeChange(Value: TProtocolType);<br />
function ReceiveData: string; //Can be used to get welcome message etc.<br />
procedure SendData(Data: string);<br />
procedure ServerTypeChange(Value: TServerType);<br />
public<br />
{All output generated during the entire session up to now}<br />
property AllOutput: string read GetSessionLog;<br />
{True if connected to server}<br />
property Connected: boolean read FConnected;<br />
{Name or IP address of host to connect to}<br />
property HostName: string read FTargetHost write FTargetHost;<br />
{Port on host used for connection. If left as 0, it will be determined by protocol type (22 for SSH, 23 for Telnet}<br />
property Port: String read FTargetPort write FTargetPort;<br />
{Location of private key file.}<br />
property PrivateKeyFile: string read GetPrivateKeyFile write SetPrivateKeyFile;<br />
{Telnet login prompt}<br />
property TelnetLoginPrompt: string read FTelnetLoginPrompt write FTelnetLoginPrompt;<br />
{Telnet password prompt}<br />
property TelnetPasswordPrompt: string read FTelnetPasswordPrompt write FTelnetPasswordPrompt;<br />
{Username used when connecting}<br />
property UserName: string read FUserName write FUserName;<br />
{Password used when connecting. Used as passphrase if PrivateKey is used}<br />
property Password: string read FPassword write FPassword;<br />
{Should we talk Telnet or SSH to the server? Defaults to SSH.}<br />
property ProtocolType: TProtocolType read FProtocolType write ProtocolTypeChange;<br />
{Windows or Unix/Linux server? Has effect on line endings. Defaults to Unix. NOTE: untested}<br />
property Servertype: TServerType read FServerType write ServerTypeChange;<br />
{Initial message displayed on logon}<br />
property WelcomeMessage: string read FWelcomeMessage;<br />
{Connect/logon to server. Requires that all authentication, protocol and hostname/port options are correct<br />
Returns descriptive result. You can then use the Connected property.}<br />
function Connect: string;<br />
{If connected, logoff from server}<br />
procedure Disconnect;<br />
{Send command to server and receive result}<br />
function CommandResult(Command: string): string; //Send command and get results<br />
constructor Create;<br />
destructor Destroy; override;<br />
end;<br />
<br />
implementation<br />
<br />
<br />
{ TelnetSSHClient }<br />
procedure TTelnetSSHClient.SetPrivateKeyFile(value: string);<br />
begin<br />
Sock.SSL.PrivateKeyFile := value;<br />
end;<br />
<br />
function TTelnetSSHClient.GetPrivateKeyFile: string;<br />
begin<br />
Result := Sock.SSL.PrivateKeyFile;<br />
end;<br />
<br />
procedure TTelnetSSHClient.DetermineLineEnding;<br />
begin<br />
case FProtocolType of<br />
SSH:<br />
begin<br />
if FServerType = Unix then<br />
FServerLineEnding := #10 //Unix<br />
else<br />
FServerLineEnding := #13 + #10; //windows<br />
end;<br />
Telnet:<br />
begin<br />
if FServerType = Unix then<br />
FServerLineEnding := #10 //Unix<br />
else<br />
FServerLineEnding := #13 + #10; //windows<br />
end;<br />
else<br />
raise Exception.Create('Unknown protocol type');<br />
end;<br />
end;<br />
<br />
procedure Ttelnetsshclient.DeterminePort;<br />
begin<br />
if FTargetPort = '' then<br />
//Set default port for protocol<br />
begin<br />
case FProtocolType of<br />
Telnet: FTargetPort := '23';<br />
SSH: FTargetPort := '22';<br />
else<br />
raise Exception.Create('Unknown protocol type.');<br />
end;<br />
<br />
end;<br />
end;<br />
<br />
procedure TTelnetSSHClient.ServerTypeChange(Value: Tservertype);<br />
begin<br />
FServerType := Value;<br />
DetermineLineEnding;<br />
end;<br />
<br />
function TTelnetSSHClient.Connect: string;<br />
var<br />
Received: string;<br />
begin<br />
result:='Unknown error while connecting';<br />
FOutputPosition := 1; //First character in output stream<br />
FWelcomeMessage := '';<br />
//Just to make sure:<br />
DetermineLineEnding;<br />
DeterminePort;<br />
if FTargetPort='0' then<br />
begin<br />
result:='Port may not be 0.';<br />
exit; //jump out of function<br />
end;<br />
case FProtocolType of<br />
Telnet:<br />
begin<br />
try<br />
if Login then<br />
begin<br />
FConnected := True;<br />
result:='Connected to telnet server.';<br />
end<br />
else<br />
if Sock.LastError<>0 then raise Exception.Create(Sock.LastErrorDesc);<br />
except<br />
on E: Exception do<br />
begin<br />
FConnected:=false;<br />
result:='Error connecting to telnet server '+FTargetHost+':'+<br />
FTargetPort+' as user ' + FUserName +<br />
'. Technical details: '+E.Message;<br />
end;<br />
end;<br />
end;<br />
SSH:<br />
begin<br />
{$IFNDEF HAS_SSH_SUPPORT}<br />
raise Exception.Create(<br />
'SSH support has not been compiled into the telnetsshclient library.');<br />
{$ENDIF HAS_SSH_SUPPORT}<br />
try<br />
if (PrivateKeyFile <> '') and (FPassword <> '') then<br />
Sock.SSL.KeyPassword:=FPassword;<br />
if SSHLogin then<br />
begin<br />
FConnected := True;<br />
result:='Connected to SSH server.';<br />
end<br />
else<br />
begin<br />
if Sock.LastError<>0 then raise Exception.Create(Sock.LastErrorDesc);<br />
if Sock.SSL.LastError<0 then raise Exception.Create(Sock.SSL.LastErrorDesc);<br />
end;<br />
except<br />
on E: Exception do<br />
begin<br />
FConnected:=false;<br />
result:='Error connecting to SSH server '+FTargetHost+':'+<br />
FTargetPort+' as user ' + FUserName +<br />
'. Technical details: '+E.Message;<br />
end;<br />
end;<br />
end;<br />
else<br />
raise Exception.Create('Unknown protocol type');<br />
end;<br />
if FConnected = True then<br />
begin<br />
FWelcomeMessage := ReceiveData;<br />
if FProtocolType=Telnet then<br />
begin<br />
//Unfortunately, we'll have to extract login ourselves<br />
//Hope it applies to all server types.<br />
if (AnsiPos(AnsiLowerCase(FTelnetLoginPrompt),AnsiLowerCase(FWelcomeMessage))>0) then<br />
begin<br />
SendData(UserName);<br />
end;<br />
Received:=ReceiveData;<br />
if (AnsiPos(AnsiLowerCase(FTelnetPasswordPrompt),AnsiLowerCase(Received))>0) then<br />
begin<br />
SendData(Password);<br />
end;<br />
//Receive additional welcome message/message of the day<br />
FWelcomeMessage:=FWelcomeMessage+LineEnding+ReceiveData;<br />
end;<br />
end;<br />
end;<br />
<br />
procedure TTelnetSSHClient.Disconnect;<br />
begin<br />
Logout;<br />
FConnected := False;<br />
end;<br />
<br />
function TTelnetSSHClient.ReceiveData: string;<br />
begin<br />
Result := '';<br />
while Sock.CanRead(1000) or (Sock.WaitingData > 0) do<br />
begin<br />
Sock.RecvPacket(1000);<br />
Result := Result + Copy(SessionLog, FOutputPosition,<br />
Length(SessionLog));<br />
FOutputPosition := Length(SessionLog) + 1;<br />
end;<br />
end;<br />
<br />
procedure Ttelnetsshclient.SendData(Data: String);<br />
begin<br />
Data := Data + FServerLineEnding; //Could be linux, could be Windows<br />
Send(Data);<br />
end;<br />
<br />
function TTelnetSSHClient.GetSessionLog: string;<br />
begin<br />
// Gets complete output up to now<br />
Result := SessionLog;<br />
end;<br />
<br />
procedure TTelnetSSHClient.ProtocolTypeChange(Value: Tprotocoltype);<br />
begin<br />
FProtocolType := Value;<br />
//Auto-determine port and line ending, if necessary<br />
DeterminePort;<br />
DetermineLineEnding;<br />
end;<br />
<br />
function TTelnetSSHClient.CommandResult(Command: string): string;<br />
begin<br />
Result := '';<br />
if FConnected then<br />
begin<br />
SendData(Command);<br />
Result := ReceiveData; //gets too much<br />
end<br />
else<br />
begin<br />
//raise exception<br />
Result := '';<br />
raise Exception.Create('Can only run command when connected');<br />
end;<br />
end;<br />
<br />
constructor TTelnetSSHClient.Create;<br />
begin<br />
inherited;<br />
FConnected := False;<br />
FProtocolType := SSH; //Could be telnet, too<br />
FServerType := Unix; //Probably a safe default.<br />
FTelnetLoginPrompt := 'login:';<br />
FTelnetPasswordPrompt := 'password:';<br />
DetermineLineEnding;<br />
DeterminePort;<br />
end;<br />
<br />
destructor TTelnetSSHClient.Destroy;<br />
begin<br />
if FConnected then<br />
Disconnect;<br />
inherited Destroy;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
=== Example client code ===<br />
<br />
To use the TTelnetSSHClient class we just made, you can use this example application, sshtest.lpr. Note that it needs to be compiled by Lazarus as it needs the LCL components to work with Synapse:<br />
<br />
<syntaxhighlight lang="pascal"><br />
program sshtest;<br />
<br />
{Test program for telnetsshclient<br />
<br />
Written by Reinier Olislagers 2011.<br />
Modified for libssh2 by Alexey Suhinin 2012.<br />
<br />
License of code:<br />
* MIT<br />
* LGPLv2 or later (with FreePascal static linking exception)<br />
* GPLv2 or later<br />
according to your choice.<br />
Free use allowed but please don't sue or blame me.<br />
<br />
Uses other libraries/components; different licenses may apply that also can influence the combined/compiled work.<br />
<br />
Run: sshtest <serverIPorhostname> [PrivateKeyFile]<br />
}<br />
{$mode objfpc}{$H+}<br />
{$APPTYPE CONSOLE}<br />
<br />
uses<br />
telnetsshclient;<br />
var<br />
comm: TTelnetSSHClient;<br />
Command: string;<br />
begin<br />
writeln('Starting.');<br />
comm:=TTelnetSSHClient.Create;<br />
comm.HostName:= ParamStr(1); //First argument on command line<br />
if comm.HostName='' then<br />
begin<br />
writeln('Please specify hostname on command line.');<br />
halt(1);<br />
end;<br />
<br />
comm.PrivateKeyFile := ParamStr(2);<br />
<br />
comm.TargetPort:='0'; //auto determine based on protocoltype<br />
comm.UserName:='root'; //change to your situation<br />
comm.Password:='password'; //change to your situation<br />
comm.ProtocolType:=SSH; //Telnet or SSH<br />
writeln(comm.Connect); //Show result of connection<br />
if comm.Connected then<br />
begin<br />
writeln('Server: ' + comm.HostName + ':'+comm.TargetPort+', user: '+comm.UserName);<br />
writeln('Welcome message:');<br />
writeln(comm.WelcomeMessage);<br />
Command:='ls -al';<br />
writeln('*** Sending ' + Command);<br />
writeln('*** Begin result****');<br />
writeln(comm.CommandResult(Command));<br />
writeln('*** End result****');<br />
writeln('');<br />
writeln('');<br />
Command:='df -h';<br />
writeln('*** Sending ' + Command);<br />
writeln('*** Begin result****');<br />
writeln(comm.CommandResult(Command));<br />
writeln('*** End result****');<br />
writeln('');<br />
writeln('');<br />
writeln('All output:');<br />
writeln('*** Begin result****');<br />
writeln(comm.AllOutput);<br />
writeln('*** End result****');<br />
comm.Disconnect;<br />
end<br />
else<br />
begin<br />
writeln('Connection to ' +<br />
comm.HostName + ':' +<br />
comm.TargetPort + ' failed.');<br />
end;<br />
comm.Free;<br />
end.<br />
</syntaxhighlight><br />
<br />
== OAuth v1/Twitter/Plurk integration ==<br />
<br />
An OAuth v1 library written in FPC that uses Synapse (and is ready for other network libraries like lNet) is available in [[fpctwit]]. FPCtwit also contains FPC Twitter and Plurk example client programs and a Lazarus Twitter client.<br />
<br />
== Other Web and Networking Articles ==<br />
<br />
* [[Portal:Web Development|Web Development Portal]]<br />
* [[Networking]]<br />
* [[Networking libraries]] - comparison of various networking libraries<br />
* [[Brook Framework]] - The perfect Free Pascal framework for your web applications. It's pure Pascal. You don't need to leave your preferred programming language.<br />
* [[Sockets]] - TCP/IP Sockets components<br />
* [[fcl-net]] - Networking library supplied with FPC<br />
* [[lNet]] - Lightweight Networking Components<br />
* [[XML Tutorial]] - XML is often utilized on network communications<br />
* [[FPC and Apache Modules]]<br />
* [[fcl-web]] - Also known as fpWeb, this is a library to develop web applications which can be deployed as cgi, fastcgi or apache modules.<br />
* [[Secure programming | Secure Programming]]<br />
* [[Internet Tools]] - A wrapper around Synapse/wininet/Android's http components simplifying https and redirections, and a XPath/XQuery/CSS Selector/JSONiq engine to process the downloaded pages<br />
<br />
== See also ==<br />
<br />
* [[Download from SourceForge]] Example that uses Synapse to download from an HTTP server that redirects.<br />
* [https://sourceforge.net/projects/visualsynapse/ Visual Synapse] component wrappers for many parts of Synapse serial and networking library (TvsComPort, TvsWebClient, TvsSniffer, TvsHTTPServer, TvsFTPServer, TvsAuthentication, TvsVisualDNS, TvsVisualHTTP, TvsVisualDUP, TvsVisualTCP, TvsVisualICMP, TvsSocksProxyInfo, TvsIPHelper, TvsSendMail and TvsSynPing).<br />
* [https://forum.lazarus.freepascal.org/index.php/topic,48677.0.html TCP/IP component based on Synapse + a small demo application]<br />
<br />
== External links ==<br />
<br />
* [http://www.ararat.cz/synapse/ Official site]<br />
* [http://synapse.ararat.cz/doc/help/ Official documentation]<br />
* [https://github.com/geby/synapse/wiki Download and Wiki]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Synapse&diff=158251Synapse2024-02-20T01:24:23Z<p>Dbannon: typo</p>
<hr />
<div>{{Synapse}}<br />
<br />
Synapse provides an easy to use serial port and synchronous TCP/IP library.<br />
<br />
__TOC__<br />
<br />
== Overview ==<br />
<br />
Synapse offers [[Hardware_Access#Synaser|serial]] port and TCP/IP connectivity. It differs from other libraries that you only require to add some Synapse Pascal source code files to your code; no need for installing packages etc. The only exception is that you will need an external crypto library if you want to use encryption such as SSL/TLS/SSH. <br />
<br />
See the documentation on the official site (link below) for more details.<br />
<br />
== Installation ==<br />
<br />
Installation can be as simple as simply copying over all files to your application directory and adding the relevant Synapse units to your ''uses'' clause.<br />
<br />
A more elegant and recommended way is compiling the laz_synapse.lpk package so you can use the same units in all your projects.<br />
<br />
Synapse download / Wiki info page: [https://github.com/geby/synapse Download and Wiki]<br />
<br />
== Support and bug reporting ==<br />
<br />
The Synapse project has a mailing list where support is given and patches can be submitted.<br />
<br />
Bug reports can also be mailed to the mailing list.<br />
<br />
See the [http://www.ararat.cz/synapse/doku.php/support Synapse support page]<br />
<br />
== SSL/TLS support ==<br />
<br />
You can use OpenSSL, CryptLib, StreamSecII or OpenStreamSecII SSL support with Synapse. By default, no SSL support is used. <br />
<br />
The support is activated by putting the chosen unit name in the uses section in your project. You also have to put the binary library file in your project path (Windows), or install it into your library search path (Linux, macOS, FreeBSD). <br />
<br />
Synapse loads SSL library files in runtime as dynamic libraries.<br />
<br />
* For detailed information refer to [http://www.ararat.cz/synapse/doku.php/public:howto:sslplugin SSL/TLS Plugin Architecture]<br />
* Some crypt libraries can be obtained from: http://synapse.ararat.cz/files/crypt/<br />
<br />
=== Missing library ===<br />
<br />
On Linux you need to make sure the required dynamic library is present/installed on your system. In case of cryptlib if the library is not present on the system, an error message appears during linking:<br />
<br />
/usr/bin/ld: cannot find -lcl<br />
<br />
A similar message will be displayed when using other dynamic libraries.<br />
<br />
== Web server example ==<br />
<br />
See the [[Networking#Webserver example|Webserver example]].<br />
<br />
== QOTD server query example ==<br />
<br />
See the [[QOTD|Quote of the Day server query example]]. <br />
<br />
== Sending email ==<br />
<br />
Refer to the [[Synapse - Email Examples]] article.<br />
<br />
== Downloading files ==<br />
<br />
=== From an FTP server ===<br />
<br />
Given an URL and a (path and) file name, this will download it from an FTP server.<br />
It's mostly a wrapper around the Synapse code meant to make downloading easier when handling arbitrary files.<br />
If you know exactly what you're going to download where, just a call to Synapse:<br />
<br />
<syntaxhighlight lang="pascal"><br />
FtpGetFile<br />
</syntaxhighlight><br />
<br />
will get you very far.<br />
<br />
<syntaxhighlight lang="pascal"><br />
function DownloadFTP(URL, TargetFile: string): boolean;<br />
const<br />
FTPPort=21;<br />
FTPScheme='ftp://'; //URI scheme name for FTP URLs<br />
var<br />
Host: string;<br />
Port: integer;<br />
Source: string;<br />
FoundPos: integer;<br />
begin<br />
// Strip out scheme info:<br />
if LeftStr(URL, length(FTPScheme))=FTPScheme then URL:=Copy(URL, length(FTPScheme)+1, length(URL));<br />
<br />
// Crude parsing; could have used URI parsing code in FPC packages...<br />
FoundPos:=pos('/', URL);<br />
Host:=LeftStr(URL, FoundPos-1);<br />
Source:=Copy(URL, FoundPos+1, Length(URL));<br />
<br />
//Check for port numbers:<br />
FoundPos:=pos(':', Host);<br />
Port:=FTPPort;<br />
if FoundPos>0 then<br />
begin<br />
Host:=LeftStr(Host, FoundPos-1);<br />
Port:=StrToIntDef(Copy(Host, FoundPos+1, Length(Host)),21);<br />
end;<br />
Result:=FtpGetFile(Host, IntToStr(Port), Source, TargetFile, 'anonymous', 'fpc@example.com');<br />
if result=false then writeln('DownloadFTP: error downloading '+URL+'. Details: host: '+Host+'; port: '+Inttostr(Port)+'; remote path: '+Source+' to '+TargetFile);<br />
end;<br />
</syntaxhighlight><br />
<br />
Example to get list of files in given path<br />
<br />
<syntaxhighlight lang="pascal"><br />
//Use ftpsend unit<br />
<br />
function FtpGetDir(const IP, Port, Path, User, Pass: string; DirList: TStringList): Boolean;<br />
var<br />
i: Integer;<br />
s: string;<br />
begin<br />
Result := False;<br />
with TFTPSend.Create do<br />
try<br />
Username := User;<br />
Password := Pass;<br />
TargetHost := IP;<br />
TargetPort := Port;<br />
if not Login then<br />
Exit;<br />
Result := List(Path, False);<br />
for i := 0 to FtpList.Count -1 do<br />
begin<br />
s := FTPList[i].FileName;<br />
DirList.Add(s);<br />
end;<br />
Logout;<br />
finally<br />
Free;<br />
end;<br />
end; <br />
</syntaxhighlight><br />
<br />
=== From an HTTP server ===<br />
<br />
Given an URL and a (path and) file name, this will download it from an HTTP server.<br />
Note that this code checks the HTTP status code (like 200, 404) to see if the document we got back from the server is the desired file or an error page.<br />
<br />
==== Simple version ====<br />
<br />
<syntaxhighlight lang="pascal"><br />
...<br />
uses httpsend,<br />
...<br />
function DownloadHTTP(URL, TargetFile: string): Boolean;<br />
var<br />
HTTPGetResult: Boolean;<br />
HTTPSender: THTTPSend;<br />
begin<br />
Result := False;<br />
HTTPSender := THTTPSend.Create;<br />
try<br />
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);<br />
if (HTTPSender.ResultCode >= 100) and (HTTPSender.ResultCode<=299) then begin<br />
HTTPSender.Document.SaveToFile(TargetFile);<br />
Result := True;<br />
end; <br />
finally<br />
HTTPSender.Free;<br />
end;<br />
end;</syntaxhighlight><br />
<br />
==== Advanced version ====<br />
<br />
<syntaxhighlight lang="pascal"><br />
...<br />
uses httpsend<br />
...<br />
function DownloadHTTP(URL, TargetFile: string): Boolean;<br />
// Download file; retry if necessary.<br />
// Could use Synapse HttpGetBinary, but that doesn't deal<br />
// with result codes (i.e. it happily downloads a 404 error document)<br />
const<br />
MaxRetries = 3;<br />
var<br />
HTTPGetResult: Boolean;<br />
HTTPSender: THTTPSend;<br />
RetryAttempt: Integer;<br />
begin<br />
Result := False;<br />
RetryAttempt := 1;<br />
HTTPSender := THTTPSend.Create;<br />
try<br />
try<br />
// Try to get the file<br />
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);<br />
while (HTTPGetResult = False) and (RetryAttempt < MaxRetries) do<br />
begin<br />
Sleep(500 * RetryAttempt);<br />
HTTPSender.Clear;<br />
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);<br />
RetryAttempt := RetryAttempt + 1;<br />
end;<br />
// If we have an answer from the server, check if the file<br />
// was sent to us.<br />
case HTTPSender.Resultcode of<br />
100..299:<br />
begin<br />
HTTPSender.Document.SaveToFile(TargetFile);<br />
Result := True;<br />
end; //informational, success<br />
300..399: Result := False; // redirection. Not implemented, but could be.<br />
400..499: Result := False; // client error; 404 not found etc<br />
500..599: Result := False; // internal server error<br />
else Result := False; // unknown code<br />
end;<br />
except<br />
// We don't care for the reason for this error; the download failed.<br />
Result := False;<br />
end;<br />
finally<br />
HTTPSender.Free;<br />
end;<br />
end;<br />
</syntaxhighlight><br />
<br />
==== Simple version with progress ====<br />
<br />
The following example shows how to get progress information from the HTTP download, as well as the file size.<br />
The file size is retrieved from the header information. <br />
<br />
<syntaxhighlight lang="pascal"><br />
unit uhttpdownloader;<br />
<br />
// Essential to change this. The default is {$mode objfpc}{$H+} and does not work.<br />
{$mode Delphi}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, httpsend, blcksock, typinfo;<br />
<br />
type<br />
IProgress = interface<br />
procedure ProgressNotification(Text: String; CurrentProgress : integer; MaxProgress : integer);<br />
end;<br />
<br />
type<br />
{ THttpDownloader }<br />
<br />
THttpDownloader = class<br />
public<br />
function DownloadHTTP(URL, TargetFile: string; ProgressMonitor : IProgress): Boolean;<br />
private<br />
Bytes : Integer;<br />
MaxBytes : Integer;<br />
HTTPSender: THTTPSend;<br />
ProgressMonitor : IProgress;<br />
procedure Status(Sender: TObject; Reason: THookSocketReason; const Value: String);<br />
function GetSizeFromHeader(Header: String):integer;<br />
end;<br />
<br />
implementation<br />
<br />
function THttpDownloader.DownloadHTTP(URL, TargetFile: string; ProgressMonitor : IProgress): Boolean;<br />
var<br />
HTTPGetResult: Boolean;<br />
begin<br />
Result := False;<br />
Bytes:= 0;<br />
MaxBytes:= -1;<br />
Self.ProgressMonitor:= ProgressMonitor;<br />
<br />
HTTPSender := THTTPSend.Create;<br />
try<br />
HTTPSender.Sock.OnStatus:= Status;<br />
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);<br />
if (HTTPSender.ResultCode >= 100) and (HTTPSender.ResultCode<=299) then begin<br />
HTTPSender.Document.SaveToFile(TargetFile);<br />
Result := True;<br />
end;<br />
finally<br />
HTTPSender.Free;<br />
end;<br />
end;<br />
<br />
procedure THttpDownloader.Status(Sender: TObject; Reason: THookSocketReason; const Value: String);<br />
var<br />
V, currentHeader: String;<br />
i: integer;<br />
begin<br />
//try to get filesize from headers<br />
if (MaxBytes = -1) then<br />
begin<br />
for i:= 0 to HTTPSender.Headers.Count - 1 do<br />
begin<br />
currentHeader:= HTTPSender.Headers[i];<br />
MaxBytes:= GetSizeFromHeader(currentHeader);<br />
if MaxBytes <> -1 then break;<br />
end;<br />
end;<br />
<br />
V := GetEnumName(TypeInfo(THookSocketReason), Integer(Reason)) + ' ' + Value;<br />
<br />
if Reason = THookSocketReason.HR_ReadCount then<br />
begin<br />
Bytes:= Bytes + StrToInt(Value);<br />
ProgressMonitor.ProgressNotification(V, Bytes, MaxBytes);<br />
end;<br />
end;<br />
<br />
function THttpDownloader.GetSizeFromHeader(Header: String): integer;<br />
var<br />
item : TStringList;<br />
begin<br />
Result:= -1;<br />
<br />
if Pos('Content-Length:', Header) <> 0 then<br />
begin<br />
item:= TStringList.Create();<br />
item.Delimiter:= ':';<br />
item.StrictDelimiter:=true;<br />
item.DelimitedText:=Header;<br />
if item.Count = 2 then<br />
begin<br />
Result:= StrToInt(Trim(item[1]));<br />
end;<br />
end;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
What are we doing here?<br />
<br />
First of all we look into the headers to get the file size. We have to wait and check if the header is there. The first events do not contain the Content-Length: information.<br />
<br />
Once found, we extract that information. There are several events popping up here, which you can react to. But we only check for THookSocketReason.HR_ReadCount in that example.<br />
<br />
"HR_ReadCount" provides us with the information how many bytes where read since the last event.<br />
<br />
The progress is then reported to the UI:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.ProgressNotification(Text: String; CurrentProgress: integer; MaxProgress: integer);<br />
begin<br />
if (MaxProgress <> -1) then<br />
begin<br />
ProgressBar.Max:= MaxProgress;<br />
end;<br />
<br />
ProgressBar.Position:= CurrentProgress;<br />
memoStatus.Lines.Add(Text);<br />
Application.ProcessMessages;<br />
end;<br />
</syntaxhighlight><br />
<br />
So, the final main unit will be:<br />
<br />
<syntaxhighlight lang="pascal"><br />
unit uMain;<br />
<br />
{$mode Delphi}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls, httpsend, blcksock, typinfo,<br />
uhttpdownloader;<br />
<br />
type<br />
<br />
{ TMainForm }<br />
<br />
TMainForm = class(TForm, IProgress)<br />
btnStartDownload: TButton;<br />
edtUrl: TEdit;<br />
labelUrl: TLabel;<br />
memoStatus: TMemo;<br />
ProgressBar: TProgressBar;<br />
SaveDialog: TSaveDialog;<br />
procedure btnStartDownloadClick(Sender: TObject);<br />
private<br />
{ private declarations }<br />
function GetFileNameFromURL(url: String):string;<br />
public<br />
{ public declarations }<br />
procedure ProgressNotification(Text: String; CurrentProgress : integer; MaxProgress : integer);<br />
end;<br />
<br />
var<br />
MainForm: TMainForm;<br />
<br />
implementation<br />
<br />
{$R *.lfm}<br />
<br />
{ TMainForm }<br />
<br />
procedure TMainForm.btnStartDownloadClick(Sender: TObject);<br />
var<br />
fileName: String;<br />
downloader: THttpDownloader;<br />
success: boolean;<br />
begin<br />
fileName:= GetFileNameFromURL(edtUrl.Text);<br />
SaveDialog.FileName:=fileName;<br />
if (SaveDialog.Execute) then<br />
begin<br />
memoStatus.Lines.Clear;<br />
ProgressBar.Position:=0;<br />
downloader:= THttpDownloader.Create();<br />
success:= downloader.DownloadHTTP(edtUrl.Text, SaveDialog.FileName, Self);<br />
<br />
ProgressBar.Position:=0;<br />
if Success then<br />
memoStatus.Lines.Add('Download successful')<br />
else<br />
memoStatus.Lines.Add('Error during download');<br />
<br />
end;<br />
end;<br />
<br />
function TMainForm.GetFileNameFromURL(url: String): string;<br />
var i, l : integer;<br />
fileName, current : String;<br />
begin<br />
fileName:= '';<br />
l:= Length(url);<br />
for i:= l downto 0 do begin<br />
current:= url[i];<br />
if current <> '/' then<br />
begin<br />
fileName:= current + fileName;<br />
end else begin<br />
Result:= fileName;<br />
break;<br />
end;<br />
end;<br />
end;<br />
<br />
procedure TMainForm.ProgressNotification(Text: String; CurrentProgress: integer; MaxProgress: integer);<br />
begin<br />
if (MaxProgress <> -1) then ProgressBar.Max:= MaxProgress;<br />
ProgressBar.Position:= CurrentProgress;<br />
memoStatus.Lines.Add(Text);<br />
Application.ProcessMessages;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
Reference: https://andydunkel.net/2015/09/09/lazarus_synapse_progress/<br />
<br />
==== From an HTTP server by parsing URLs: Sourceforge ====<br />
<br />
Please see [[Download from SourceForge]] for an example of downloading from sourceforge.net.<br />
<br />
=== From an HTTPS server ===<br />
<br />
This is similar to downloading from an HTTP server. In addition you need to [[Synapse#SSL.2FTLS_support|activate SSL/TLS support]] and obtain the binary file(s) for the needed library. Then you can use the same DownloadHTTP function for downloading a file from a URL starting with '''https://'''.<br />
<br />
== SSH/Telnet client sample program ==<br />
<br />
Below you will find a unit that allows you to use telnet/SSH client functionality that uses the synapse tlntsend.pas unit. An example program shows how to use this.<br />
A different, simpler way is illustrated by Leonardo Ramé at [http://leonardorame.blogspot.com/2010/01/synapse-based-ssh-client.html]. His example cannot use telnet and only sends one command, though.<br />
<br />
=== Requirements ===<br />
<br />
Apart from the Synapse sources (of which you only need a couple), if you want to use SSH functionality, you will need an encryption library that Synapse uses. If you only use Telnet, you don't need this.<br />
<br />
There are 2 choices:<br />
* Cryptlib library. Advantage: stable. Apparently able to use private keys but these are in some format that is not widely supported.<br />
* LibSSH2 library. Pascal bindings still in development, but you can use a file with your private key (in OpenSSH format) to authenticate.<br />
<br />
==== Cryptlib ====<br />
<br />
* On Windows, download a binary version of the cryptlib DLL (CL32.DLL) and put it in your source directory. If you compile to a different directory or distribute your program, you will need to distribute the DLL as well.<br />
* On Linux and OSX, install cryptlib via your package manager/other means. When distributing your application, mark cryptlib as a requirement in your .deb/.rpm/whatever package.<br />
<br />
You will also need the bindings (cryptlib.pas), present in the source distribution of cryptlib.<br />
<br />
The versions of the cryptlib binary and the bindings must match.<br />
<br />
{{Note|It seems that cryptlib is not suitable to connect to linux machines, though AIX works. Use SSH2 instead.}}<br />
<br />
==== LibSSH2 ====<br />
<br />
* On Windows, download a binary version of the libssh2 DLL (LIBSSH2.DLL) and put it in your source directory. If you compile to a different directory or distribute your program, you will need to distribute the DLL as well.<br />
* On Linux and macOS, install libssh2 via your package manager/other means. When distributing your application:<br />
** Linux: mark libssh2 as a requirement in your .deb/.rpm/whatever package.<br />
** macOS: include libssh2 in your [[Application Bundle]]'s Resources directory.<br />
<br />
You will also need ssl_libssh2.pas (see below) and the bindings: (libssh2.pas, see [http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465 this forum post]). The libssh2 binary and the bindings must match.<br />
<br />
=== Synapse libssh2 SSL plugin ===<br />
<br />
{{Note| plugin is not completed.}}<br />
<br />
<syntaxhighlight lang="pascal"><br />
{<br />
ssl_libssh2.pas version 0.2<br />
<br />
SSH2 support (draft) plugin for Synapse Library (http://www.ararat.cz/synapse) by LibSSH2 (http://libssh2.org)<br />
Requires: libssh2 pascal interface - http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465 and<br />
libssh2.dll with OpenSSL.<br />
<br />
(С) Alexey Suhinin http://x-alexey.narod.ru<br />
}<br />
<br />
{$IFDEF FPC}<br />
{$MODE DELPHI}<br />
{$ENDIF}<br />
{$H+}<br />
<br />
unit ssl_libssh2;<br />
<br />
interface<br />
<br />
uses<br />
SysUtils,<br />
blcksock, synsock,<br />
libssh2;<br />
<br />
type<br />
{:@abstract(class implementing CryptLib SSL/SSH plugin.)<br />
Instance of this class will be created for each @link(TTCPBlockSocket).<br />
You not need to create instance of this class, all is done by Synapse itself!}<br />
TSSLLibSSH2 = class(TCustomSSL)<br />
protected<br />
FSession: PLIBSSH2_SESSION;<br />
FChannel: PLIBSSH2_CHANNEL;<br />
function SSHCheck(Value: integer): Boolean;<br />
function DeInit: Boolean;<br />
public<br />
{:See @inherited}<br />
constructor Create(const Value: TTCPBlockSocket); override;<br />
destructor Destroy; override;<br />
function Connect: boolean; override;<br />
function LibName: String; override;<br />
function Shutdown: boolean; override;<br />
{:See @inherited}<br />
function BiShutdown: boolean; override;<br />
{:See @inherited}<br />
function SendBuffer(Buffer: TMemory; Len: Integer): Integer; override;<br />
{:See @inherited}<br />
function RecvBuffer(Buffer: TMemory; Len: Integer): Integer; override;<br />
{:See @inherited}<br />
function WaitingData: Integer; override;<br />
{:See @inherited}<br />
function GetSSLVersion: string; override;<br />
published<br />
end;<br />
<br />
implementation<br />
<br />
{==============================================================================}<br />
function TSSLLibSSH2.SSHCheck(Value: integer): Boolean;<br />
var<br />
PLastError: PAnsiChar;<br />
ErrMsgLen: Integer;<br />
begin<br />
Result := true;<br />
FLastError := 0;<br />
FLastErrorDesc := '';<br />
if Value<0 then<br />
begin<br />
FLastError := libssh2_session_last_error(FSession, PLastError, ErrMsglen, 0);<br />
FLastErrorDesc := PLastError;<br />
Result := false;<br />
end;<br />
end;<br />
<br />
<br />
function TSSLLibSSH2.DeInit: Boolean;<br />
begin<br />
if Assigned(FChannel) then<br />
begin<br />
libssh2_channel_free(FChannel);<br />
FChannel := nil;<br />
end;<br />
if Assigned(FSession) then<br />
begin<br />
libssh2_session_disconnect(FSession,'Goodbye');<br />
libssh2_session_free(FSession);<br />
FSession := nil;<br />
end;<br />
FSSLEnabled := False;<br />
Result := true;<br />
end;<br />
<br />
constructor TSSLLibSSH2.Create(const Value: TTCPBlockSocket);<br />
begin<br />
inherited Create(Value);<br />
FSession := nil;<br />
FChannel := nil;<br />
end;<br />
<br />
destructor TSSLLibSSH2.Destroy;<br />
begin<br />
DeInit;<br />
inherited Destroy;<br />
end;<br />
<br />
function TSSLLibSSH2.Connect: boolean;<br />
begin<br />
Result := False;<br />
if SSLEnabled then DeInit;<br />
if (FSocket.Socket <> INVALID_SOCKET) and (FSocket.SSL.SSLType = LT_SSHv2) then<br />
begin<br />
FSession := libssh2_session_init();<br />
if not Assigned(FSession) then<br />
begin<br />
FLastError := -999;<br />
FLastErrorDesc := 'Cannot initialize SSH session';<br />
exit;<br />
end;<br />
if not SSHCheck(libssh2_session_startup(FSession, FSocket.Socket)) then<br />
exit;<br />
if (FSocket.SSL.PrivateKeyFile<>'') then<br />
begin<br />
if (not SSHCheck(libssh2_userauth_publickey_fromfile(FSession, PChar(FSocket.SSL.Username), nil, PChar(FSocket.SSL.PrivateKeyFile), PChar(FSocket.SSL.KeyPassword)))) then<br />
exit;<br />
end<br />
else<br />
if (FSocket.SSL.Username<>'') and (FSocket.SSL.Password<>'') then<br />
begin<br />
if (not SSHCheck(libssh2_userauth_password(FSession, PChar(FSocket.SSL.Username), PChar(FSocket.SSL.Password)))) then<br />
exit;<br />
end;<br />
FChannel := libssh2_channel_open_session(FSession);<br />
if not assigned(FChannel) then<br />
begin<br />
SSHCheck(-1); // get error<br />
if FLastError = 0 then<br />
begin<br />
FLastError := -999; // unknown error<br />
FLastErrorDesc := 'Cannot open session';<br />
end;<br />
exit;<br />
end;<br />
if not SSHCheck(libssh2_channel_request_pty(FChannel, 'vanilla')) then<br />
exit;<br />
if not SSHCheck(libssh2_channel_shell(FChannel)) then<br />
exit;<br />
FSSLEnabled := True;<br />
Result := True;<br />
end;<br />
end;<br />
<br />
function TSSLLibSSH2.LibName: String;<br />
begin<br />
Result := 'ssl_libssh2';<br />
end;<br />
<br />
function TSSLLibSSH2.Shutdown: boolean;<br />
begin<br />
Result := DeInit;<br />
end;<br />
<br />
<br />
function TSSLLibSSH2.BiShutdown: boolean;<br />
begin<br />
Result := DeInit;<br />
end;<br />
<br />
function TSSLLibSSH2.SendBuffer(Buffer: TMemory; Len: Integer): Integer;<br />
begin<br />
Result:=libssh2_channel_write(FChannel, PChar(Buffer), Len);<br />
SSHCheck(Result);<br />
end;<br />
<br />
function TSSLLibSSH2.RecvBuffer(Buffer: TMemory; Len: Integer): Integer;<br />
begin<br />
result:=libssh2_channel_read(FChannel, PChar(Buffer), Len);<br />
SSHCheck(Result);<br />
end;<br />
<br />
function TSSLLibSSH2.WaitingData: Integer;<br />
begin<br />
if libssh2_poll_channel_read(FChannel, Result) <> 1 then Result := 0;<br />
end;<br />
<br />
function TSSLLibSSH2.GetSSLVersion: string;<br />
begin<br />
Result:=libssh2_version(0);<br />
end;<br />
<br />
initialization<br />
if libssh2_init(0)=0 then<br />
SSLImplementation := TSSLLibSSH2;<br />
<br />
finalization<br />
libssh2_exit;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
=== Terminal client class ===<br />
<br />
The telnetsshclient.pas unit below wraps around the Synapse tlntsend.pas unit and abstracts logging in, sending commands and receiving output and logging out.<br />
<br />
If you only need a telnet client and can live without SSH support, comment out {$DEFINE HAS_SSH_SUPPORT} below so you don't need to have the libssh2 dll.<br />
<br />
This unit has been lightly tested on a Linux ssh/telnet server. Additional tests welcome.<br />
<br />
<syntaxhighlight lang="pascal"><br />
unit telnetsshclient;<br />
<br />
{ Wrapper around Synapse libraries and SSL library (libssh2+libssl<br />
is used right now)<br />
Download compiled Windows dll from e.g.<br />
http://alxdm.dyndns-at-work.com:808/files/windll_libssh2.zip<br />
Download FreePascal interface files:<br />
http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465<br />
<br />
This unit allows the user to send Telnet or SSH commands and get the output<br />
Thanks to Leonardo Rame<br />
http://leonardorame.blogspot.com/2010/01/synapse-based-ssh-client.html<br />
and Ludo Brands.<br />
<br />
Written by Reinier Olislagers 2011.<br />
Modified for libssh2 by Alexey Suhinin 2012.<br />
<br />
License of code:<br />
* MIT<br />
* LGPLv2 or later (with FreePascal static linking exception)<br />
* GPLv2 or later<br />
according to your choice.<br />
Free use allowed but please don't sue or blame me.<br />
<br />
Uses other libraries/components; different licenses may apply that also can influence the combined/compiled work.<br />
}<br />
<br />
{$mode objfpc}{$H+}<br />
{$DEFINE HAS_SSH_SUPPORT} //comment out if only telnet support required<br />
{$DEFINE LIBSSH2}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils,<br />
tlntsend<br />
{$IFDEF HAS_SSH_SUPPORT}<br />
{ssl - or actually ssh - libs required by tlntsend}<br />
{$IFDEF LIBSSH2}<br />
ssl_libssh2<br />
{$ELSE}<br />
ssl_cryptlib<br />
{$ENDIF}<br />
{$ENDIF HAS_SSH_SUPPORT} ;<br />
<br />
type<br />
TProtocolType = (Telnet, SSH); //Different means of connecting<br />
TServerType = (Unix, Windows); //line endings, mostly<br />
{ TelnetSSHClient }<br />
<br />
{ TTelnetSSHClient }<br />
<br />
TTelnetSSHClient = class(TTelnetSend)<br />
protected<br />
FConnected: boolean;<br />
FOutputPosition: integer; //Keeps track of position in output stream<br />
FProtocolType: TProtocolType;<br />
FServerLineEnding: string; //depends on FServerType<br />
FServerType: TServerType;<br />
FWelcomeMessage, FTelnetLoginPrompt, FTelnetPasswordPrompt: string;<br />
procedure SetPrivateKeyFile(Value: string);<br />
function GetPrivateKeyFile: string;<br />
{ Based on protocol and servertype, set expected serverside line ending}<br />
procedure DetermineLineEnding;<br />
{ Sets port if no explicit port set. Uses protocol type: SSH or telnet}<br />
procedure DeterminePort;<br />
function GetSessionLog: string;<br />
procedure ProtocolTypeChange(Value: TProtocolType);<br />
function ReceiveData: string; //Can be used to get welcome message etc.<br />
procedure SendData(Data: string);<br />
procedure ServerTypeChange(Value: TServerType);<br />
public<br />
{All output generated during the entire session up to now}<br />
property AllOutput: string read GetSessionLog;<br />
{True if connected to server}<br />
property Connected: boolean read FConnected;<br />
{Name or IP address of host to connect to}<br />
property HostName: string read FTargetHost write FTargetHost;<br />
{Port on host used for connection. If left as 0, it will be determined by protocol type (22 for SSH, 23 for Telnet}<br />
property Port: String read FTargetPort write FTargetPort;<br />
{Location of private key file.}<br />
property PrivateKeyFile: string read GetPrivateKeyFile write SetPrivateKeyFile;<br />
{Telnet login prompt}<br />
property TelnetLoginPrompt: string read FTelnetLoginPrompt write FTelnetLoginPrompt;<br />
{Telnet password prompt}<br />
property TelnetPasswordPrompt: string read FTelnetPasswordPrompt write FTelnetPasswordPrompt;<br />
{Username used when connecting}<br />
property UserName: string read FUserName write FUserName;<br />
{Password used when connecting. Used as passphrase if PrivateKey is used}<br />
property Password: string read FPassword write FPassword;<br />
{Should we talk Telnet or SSH to the server? Defaults to SSH.}<br />
property ProtocolType: TProtocolType read FProtocolType write ProtocolTypeChange;<br />
{Windows or Unix/Linux server? Has effect on line endings. Defaults to Unix. NOTE: untested}<br />
property Servertype: TServerType read FServerType write ServerTypeChange;<br />
{Initial message displayed on logon}<br />
property WelcomeMessage: string read FWelcomeMessage;<br />
{Connect/logon to server. Requires that all authentication, protocol and hostname/port options are correct<br />
Returns descriptive result. You can then use the Connected property.}<br />
function Connect: string;<br />
{If connected, logoff from server}<br />
procedure Disconnect;<br />
{Send command to server and receive result}<br />
function CommandResult(Command: string): string; //Send command and get results<br />
constructor Create;<br />
destructor Destroy; override;<br />
end;<br />
<br />
implementation<br />
<br />
<br />
{ TelnetSSHClient }<br />
procedure TTelnetSSHClient.SetPrivateKeyFile(value: string);<br />
begin<br />
Sock.SSL.PrivateKeyFile := value;<br />
end;<br />
<br />
function TTelnetSSHClient.GetPrivateKeyFile: string;<br />
begin<br />
Result := Sock.SSL.PrivateKeyFile;<br />
end;<br />
<br />
procedure TTelnetSSHClient.DetermineLineEnding;<br />
begin<br />
case FProtocolType of<br />
SSH:<br />
begin<br />
if FServerType = Unix then<br />
FServerLineEnding := #10 //Unix<br />
else<br />
FServerLineEnding := #13 + #10; //windows<br />
end;<br />
Telnet:<br />
begin<br />
if FServerType = Unix then<br />
FServerLineEnding := #10 //Unix<br />
else<br />
FServerLineEnding := #13 + #10; //windows<br />
end;<br />
else<br />
raise Exception.Create('Unknown protocol type');<br />
end;<br />
end;<br />
<br />
procedure Ttelnetsshclient.DeterminePort;<br />
begin<br />
if FTargetPort = '' then<br />
//Set default port for protocol<br />
begin<br />
case FProtocolType of<br />
Telnet: FTargetPort := '23';<br />
SSH: FTargetPort := '22';<br />
else<br />
raise Exception.Create('Unknown protocol type.');<br />
end;<br />
<br />
end;<br />
end;<br />
<br />
procedure TTelnetSSHClient.ServerTypeChange(Value: Tservertype);<br />
begin<br />
FServerType := Value;<br />
DetermineLineEnding;<br />
end;<br />
<br />
function TTelnetSSHClient.Connect: string;<br />
var<br />
Received: string;<br />
begin<br />
result:='Unknown error while connecting';<br />
FOutputPosition := 1; //First character in output stream<br />
FWelcomeMessage := '';<br />
//Just to make sure:<br />
DetermineLineEnding;<br />
DeterminePort;<br />
if FTargetPort='0' then<br />
begin<br />
result:='Port may not be 0.';<br />
exit; //jump out of function<br />
end;<br />
case FProtocolType of<br />
Telnet:<br />
begin<br />
try<br />
if Login then<br />
begin<br />
FConnected := True;<br />
result:='Connected to telnet server.';<br />
end<br />
else<br />
if Sock.LastError<>0 then raise Exception.Create(Sock.LastErrorDesc);<br />
except<br />
on E: Exception do<br />
begin<br />
FConnected:=false;<br />
result:='Error connecting to telnet server '+FTargetHost+':'+<br />
FTargetPort+' as user ' + FUserName +<br />
'. Technical details: '+E.Message;<br />
end;<br />
end;<br />
end;<br />
SSH:<br />
begin<br />
{$IFNDEF HAS_SSH_SUPPORT}<br />
raise Exception.Create(<br />
'SSH support has not been compiled into the telnetsshclient library.');<br />
{$ENDIF HAS_SSH_SUPPORT}<br />
try<br />
if (PrivateKeyFile <> '') and (FPassword <> '') then<br />
Sock.SSL.KeyPassword:=FPassword;<br />
if SSHLogin then<br />
begin<br />
FConnected := True;<br />
result:='Connected to SSH server.';<br />
end<br />
else<br />
begin<br />
if Sock.LastError<>0 then raise Exception.Create(Sock.LastErrorDesc);<br />
if Sock.SSL.LastError<0 then raise Exception.Create(Sock.SSL.LastErrorDesc);<br />
end;<br />
except<br />
on E: Exception do<br />
begin<br />
FConnected:=false;<br />
result:='Error connecting to SSH server '+FTargetHost+':'+<br />
FTargetPort+' as user ' + FUserName +<br />
'. Technical details: '+E.Message;<br />
end;<br />
end;<br />
end;<br />
else<br />
raise Exception.Create('Unknown protocol type');<br />
end;<br />
if FConnected = True then<br />
begin<br />
FWelcomeMessage := ReceiveData;<br />
if FProtocolType=Telnet then<br />
begin<br />
//Unfortunately, we'll have to extract login ourselves<br />
//Hope it applies to all server types.<br />
if (AnsiPos(AnsiLowerCase(FTelnetLoginPrompt),AnsiLowerCase(FWelcomeMessage))>0) then<br />
begin<br />
SendData(UserName);<br />
end;<br />
Received:=ReceiveData;<br />
if (AnsiPos(AnsiLowerCase(FTelnetPasswordPrompt),AnsiLowerCase(Received))>0) then<br />
begin<br />
SendData(Password);<br />
end;<br />
//Receive additional welcome message/message of the day<br />
FWelcomeMessage:=FWelcomeMessage+LineEnding+ReceiveData;<br />
end;<br />
end;<br />
end;<br />
<br />
procedure TTelnetSSHClient.Disconnect;<br />
begin<br />
Logout;<br />
FConnected := False;<br />
end;<br />
<br />
function TTelnetSSHClient.ReceiveData: string;<br />
begin<br />
Result := '';<br />
while Sock.CanRead(1000) or (Sock.WaitingData > 0) do<br />
begin<br />
Sock.RecvPacket(1000);<br />
Result := Result + Copy(SessionLog, FOutputPosition,<br />
Length(SessionLog));<br />
FOutputPosition := Length(SessionLog) + 1;<br />
end;<br />
end;<br />
<br />
procedure Ttelnetsshclient.SendData(Data: String);<br />
begin<br />
Data := Data + FServerLineEnding; //Could be linux, could be Windows<br />
Send(Data);<br />
end;<br />
<br />
function TTelnetSSHClient.GetSessionLog: string;<br />
begin<br />
// Gets complete output up to now<br />
Result := SessionLog;<br />
end;<br />
<br />
procedure TTelnetSSHClient.ProtocolTypeChange(Value: Tprotocoltype);<br />
begin<br />
FProtocolType := Value;<br />
//Auto-determine port and line ending, if necessary<br />
DeterminePort;<br />
DetermineLineEnding;<br />
end;<br />
<br />
function TTelnetSSHClient.CommandResult(Command: string): string;<br />
begin<br />
Result := '';<br />
if FConnected then<br />
begin<br />
SendData(Command);<br />
Result := ReceiveData; //gets too much<br />
end<br />
else<br />
begin<br />
//raise exception<br />
Result := '';<br />
raise Exception.Create('Can only run command when connected');<br />
end;<br />
end;<br />
<br />
constructor TTelnetSSHClient.Create;<br />
begin<br />
inherited;<br />
FConnected := False;<br />
FProtocolType := SSH; //Could be telnet, too<br />
FServerType := Unix; //Probably a safe default.<br />
FTelnetLoginPrompt := 'login:';<br />
FTelnetPasswordPrompt := 'password:';<br />
DetermineLineEnding;<br />
DeterminePort;<br />
end;<br />
<br />
destructor TTelnetSSHClient.Destroy;<br />
begin<br />
if FConnected then<br />
Disconnect;<br />
inherited Destroy;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
=== Example client code ===<br />
<br />
To use the TTelnetSSHClient class we just made, you can use this example application, sshtest.lpr. Note that it needs to be compiled by Lazarus as it needs the LCL components to work with Synapse:<br />
<br />
<syntaxhighlight lang="pascal"><br />
program sshtest;<br />
<br />
{Test program for telnetsshclient<br />
<br />
Written by Reinier Olislagers 2011.<br />
Modified for libssh2 by Alexey Suhinin 2012.<br />
<br />
License of code:<br />
* MIT<br />
* LGPLv2 or later (with FreePascal static linking exception)<br />
* GPLv2 or later<br />
according to your choice.<br />
Free use allowed but please don't sue or blame me.<br />
<br />
Uses other libraries/components; different licenses may apply that also can influence the combined/compiled work.<br />
<br />
Run: sshtest <serverIPorhostname> [PrivateKeyFile]<br />
}<br />
{$mode objfpc}{$H+}<br />
{$APPTYPE CONSOLE}<br />
<br />
uses<br />
telnetsshclient;<br />
var<br />
comm: TTelnetSSHClient;<br />
Command: string;<br />
begin<br />
writeln('Starting.');<br />
comm:=TTelnetSSHClient.Create;<br />
comm.HostName:= ParamStr(1); //First argument on command line<br />
if comm.HostName='' then<br />
begin<br />
writeln('Please specify hostname on command line.');<br />
halt(1);<br />
end;<br />
<br />
comm.PrivateKeyFile := ParamStr(2);<br />
<br />
comm.TargetPort:='0'; //auto determine based on protocoltype<br />
comm.UserName:='root'; //change to your situation<br />
comm.Password:='password'; //change to your situation<br />
comm.ProtocolType:=SSH; //Telnet or SSH<br />
writeln(comm.Connect); //Show result of connection<br />
if comm.Connected then<br />
begin<br />
writeln('Server: ' + comm.HostName + ':'+comm.TargetPort+', user: '+comm.UserName);<br />
writeln('Welcome message:');<br />
writeln(comm.WelcomeMessage);<br />
Command:='ls -al';<br />
writeln('*** Sending ' + Command);<br />
writeln('*** Begin result****');<br />
writeln(comm.CommandResult(Command));<br />
writeln('*** End result****');<br />
writeln('');<br />
writeln('');<br />
Command:='df -h';<br />
writeln('*** Sending ' + Command);<br />
writeln('*** Begin result****');<br />
writeln(comm.CommandResult(Command));<br />
writeln('*** End result****');<br />
writeln('');<br />
writeln('');<br />
writeln('All output:');<br />
writeln('*** Begin result****');<br />
writeln(comm.AllOutput);<br />
writeln('*** End result****');<br />
comm.Disconnect;<br />
end<br />
else<br />
begin<br />
writeln('Connection to ' +<br />
comm.HostName + ':' +<br />
comm.TargetPort + ' failed.');<br />
end;<br />
comm.Free;<br />
end.<br />
</syntaxhighlight><br />
<br />
== OAuth v1/Twitter/Plurk integration ==<br />
<br />
An OAuth v1 library written in FPC that uses Synapse (and is ready for other network libraries like lNet) is available in [[fpctwit]]. FPCtwit also contains FPC Twitter and Plurk example client programs and a Lazarus Twitter client.<br />
<br />
== Other Web and Networking Articles ==<br />
<br />
* [[Portal:Web Development|Web Development Portal]]<br />
* [[Networking]]<br />
* [[Networking libraries]] - comparison of various networking libraries<br />
* [[Brook Framework]] - The perfect Free Pascal framework for your web applications. It's pure Pascal. You don't need to leave your preferred programming language.<br />
* [[Sockets]] - TCP/IP Sockets components<br />
* [[fcl-net]] - Networking library supplied with FPC<br />
* [[lNet]] - Lightweight Networking Components<br />
* [[XML Tutorial]] - XML is often utilized on network communications<br />
* [[FPC and Apache Modules]]<br />
* [[fcl-web]] - Also known as fpWeb, this is a library to develop web applications which can be deployed as cgi, fastcgi or apache modules.<br />
* [[Secure programming | Secure Programming]]<br />
* [[Internet Tools]] - A wrapper around Synapse/wininet/Android's http components simplifying https and redirections, and a XPath/XQuery/CSS Selector/JSONiq engine to process the downloaded pages<br />
<br />
== See also ==<br />
<br />
* [[Download from SourceForge]] Example that uses Synapse to download from an HTTP server that redirects.<br />
* [https://sourceforge.net/projects/visualsynapse/ Visual Synapse] component wrappers for many parts of Synapse serial and networking library (TvsComPort, TvsWebClient, TvsSniffer, TvsHTTPServer, TvsFTPServer, TvsAuthentication, TvsVisualDNS, TvsVisualHTTP, TvsVisualDUP, TvsVisualTCP, TvsVisualICMP, TvsSocksProxyInfo, TvsIPHelper, TvsSendMail and TvsSynPing).<br />
* [https://forum.lazarus.freepascal.org/index.php/topic,48677.0.html TCP/IP component based on Synapse + a small demo application]<br />
<br />
== External links ==<br />
<br />
* [http://www.ararat.cz/synapse/ Official site]<br />
* [http://synapse.ararat.cz/doc/help/ Official documentation]<br />
* [https://github.com/geby/synapse/wiki Download and Wiki]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Synapse&diff=158250Synapse2024-02-20T01:22:39Z<p>Dbannon: update web site link</p>
<hr />
<div>{{Synapse}}<br />
<br />
Synapse provides an easy to use serial port and synchronous TCP/IP library.<br />
<br />
__TOC__<br />
<br />
== Overview ==<br />
<br />
Synapse offers [[Hardware_Access#Synaser|serial]] port and TCP/IP connectivity. It differs from other libraries that you only require to add some Synapse Pascal source code files to your code; no need for installing packages etc. The only exception is that you will need an external crypto library if you want to use encryption such as SSL/TLS/SSH. <br />
<br />
See the documentation on the official site (link below) for more details.<br />
<br />
== Installation ==<br />
<br />
Installation can be as simple as simply copying over all files to your application directory and adding the relevant Synapse units to your ''uses'' clause.<br />
<br />
A more elegant and recommended way is compiling the laz_synapse.lpk package so you can use the same units in all your projects.<br />
<br />
Synapse download / Wiki info page: [https://github.com/geby/synapse/wiki Download and Wiki]<br />
<br />
== Support and bug reporting ==<br />
<br />
The Synapse project has a mailing list where support is given and patches can be submitted.<br />
<br />
Bug reports can also be mailed to the mailing list.<br />
<br />
See the [http://www.ararat.cz/synapse/doku.php/support Synapse support page]<br />
<br />
== SSL/TLS support ==<br />
<br />
You can use OpenSSL, CryptLib, StreamSecII or OpenStreamSecII SSL support with Synapse. By default, no SSL support is used. <br />
<br />
The support is activated by putting the chosen unit name in the uses section in your project. You also have to put the binary library file in your project path (Windows), or install it into your library search path (Linux, macOS, FreeBSD). <br />
<br />
Synapse loads SSL library files in runtime as dynamic libraries.<br />
<br />
* For detailed information refer to [http://www.ararat.cz/synapse/doku.php/public:howto:sslplugin SSL/TLS Plugin Architecture]<br />
* Some crypt libraries can be obtained from: http://synapse.ararat.cz/files/crypt/<br />
<br />
=== Missing library ===<br />
<br />
On Linux you need to make sure the required dynamic library is present/installed on your system. In case of cryptlib if the library is not present on the system, an error message appears during linking:<br />
<br />
/usr/bin/ld: cannot find -lcl<br />
<br />
A similar message will be displayed when using other dynamic libraries.<br />
<br />
== Web server example ==<br />
<br />
See the [[Networking#Webserver example|Webserver example]].<br />
<br />
== QOTD server query example ==<br />
<br />
See the [[QOTD|Quote of the Day server query example]]. <br />
<br />
== Sending email ==<br />
<br />
Refer to the [[Synapse - Email Examples]] article.<br />
<br />
== Downloading files ==<br />
<br />
=== From an FTP server ===<br />
<br />
Given an URL and a (path and) file name, this will download it from an FTP server.<br />
It's mostly a wrapper around the Synapse code meant to make downloading easier when handling arbitrary files.<br />
If you know exactly what you're going to download where, just a call to Synapse:<br />
<br />
<syntaxhighlight lang="pascal"><br />
FtpGetFile<br />
</syntaxhighlight><br />
<br />
will get you very far.<br />
<br />
<syntaxhighlight lang="pascal"><br />
function DownloadFTP(URL, TargetFile: string): boolean;<br />
const<br />
FTPPort=21;<br />
FTPScheme='ftp://'; //URI scheme name for FTP URLs<br />
var<br />
Host: string;<br />
Port: integer;<br />
Source: string;<br />
FoundPos: integer;<br />
begin<br />
// Strip out scheme info:<br />
if LeftStr(URL, length(FTPScheme))=FTPScheme then URL:=Copy(URL, length(FTPScheme)+1, length(URL));<br />
<br />
// Crude parsing; could have used URI parsing code in FPC packages...<br />
FoundPos:=pos('/', URL);<br />
Host:=LeftStr(URL, FoundPos-1);<br />
Source:=Copy(URL, FoundPos+1, Length(URL));<br />
<br />
//Check for port numbers:<br />
FoundPos:=pos(':', Host);<br />
Port:=FTPPort;<br />
if FoundPos>0 then<br />
begin<br />
Host:=LeftStr(Host, FoundPos-1);<br />
Port:=StrToIntDef(Copy(Host, FoundPos+1, Length(Host)),21);<br />
end;<br />
Result:=FtpGetFile(Host, IntToStr(Port), Source, TargetFile, 'anonymous', 'fpc@example.com');<br />
if result=false then writeln('DownloadFTP: error downloading '+URL+'. Details: host: '+Host+'; port: '+Inttostr(Port)+'; remote path: '+Source+' to '+TargetFile);<br />
end;<br />
</syntaxhighlight><br />
<br />
Example to get list of files in given path<br />
<br />
<syntaxhighlight lang="pascal"><br />
//Use ftpsend unit<br />
<br />
function FtpGetDir(const IP, Port, Path, User, Pass: string; DirList: TStringList): Boolean;<br />
var<br />
i: Integer;<br />
s: string;<br />
begin<br />
Result := False;<br />
with TFTPSend.Create do<br />
try<br />
Username := User;<br />
Password := Pass;<br />
TargetHost := IP;<br />
TargetPort := Port;<br />
if not Login then<br />
Exit;<br />
Result := List(Path, False);<br />
for i := 0 to FtpList.Count -1 do<br />
begin<br />
s := FTPList[i].FileName;<br />
DirList.Add(s);<br />
end;<br />
Logout;<br />
finally<br />
Free;<br />
end;<br />
end; <br />
</syntaxhighlight><br />
<br />
=== From an HTTP server ===<br />
<br />
Given an URL and a (path and) file name, this will download it from an HTTP server.<br />
Note that this code checks the HTTP status code (like 200, 404) to see if the document we got back from the server is the desired file or an error page.<br />
<br />
==== Simple version ====<br />
<br />
<syntaxhighlight lang="pascal"><br />
...<br />
uses httpsend,<br />
...<br />
function DownloadHTTP(URL, TargetFile: string): Boolean;<br />
var<br />
HTTPGetResult: Boolean;<br />
HTTPSender: THTTPSend;<br />
begin<br />
Result := False;<br />
HTTPSender := THTTPSend.Create;<br />
try<br />
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);<br />
if (HTTPSender.ResultCode >= 100) and (HTTPSender.ResultCode<=299) then begin<br />
HTTPSender.Document.SaveToFile(TargetFile);<br />
Result := True;<br />
end; <br />
finally<br />
HTTPSender.Free;<br />
end;<br />
end;</syntaxhighlight><br />
<br />
==== Advanced version ====<br />
<br />
<syntaxhighlight lang="pascal"><br />
...<br />
uses httpsend<br />
...<br />
function DownloadHTTP(URL, TargetFile: string): Boolean;<br />
// Download file; retry if necessary.<br />
// Could use Synapse HttpGetBinary, but that doesn't deal<br />
// with result codes (i.e. it happily downloads a 404 error document)<br />
const<br />
MaxRetries = 3;<br />
var<br />
HTTPGetResult: Boolean;<br />
HTTPSender: THTTPSend;<br />
RetryAttempt: Integer;<br />
begin<br />
Result := False;<br />
RetryAttempt := 1;<br />
HTTPSender := THTTPSend.Create;<br />
try<br />
try<br />
// Try to get the file<br />
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);<br />
while (HTTPGetResult = False) and (RetryAttempt < MaxRetries) do<br />
begin<br />
Sleep(500 * RetryAttempt);<br />
HTTPSender.Clear;<br />
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);<br />
RetryAttempt := RetryAttempt + 1;<br />
end;<br />
// If we have an answer from the server, check if the file<br />
// was sent to us.<br />
case HTTPSender.Resultcode of<br />
100..299:<br />
begin<br />
HTTPSender.Document.SaveToFile(TargetFile);<br />
Result := True;<br />
end; //informational, success<br />
300..399: Result := False; // redirection. Not implemented, but could be.<br />
400..499: Result := False; // client error; 404 not found etc<br />
500..599: Result := False; // internal server error<br />
else Result := False; // unknown code<br />
end;<br />
except<br />
// We don't care for the reason for this error; the download failed.<br />
Result := False;<br />
end;<br />
finally<br />
HTTPSender.Free;<br />
end;<br />
end;<br />
</syntaxhighlight><br />
<br />
==== Simple version with progress ====<br />
<br />
The following example shows how to get progress information from the HTTP download, as well as the file size.<br />
The file size is retrieved from the header information. <br />
<br />
<syntaxhighlight lang="pascal"><br />
unit uhttpdownloader;<br />
<br />
// Essential to change this. The default is {$mode objfpc}{$H+} and does not work.<br />
{$mode Delphi}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, httpsend, blcksock, typinfo;<br />
<br />
type<br />
IProgress = interface<br />
procedure ProgressNotification(Text: String; CurrentProgress : integer; MaxProgress : integer);<br />
end;<br />
<br />
type<br />
{ THttpDownloader }<br />
<br />
THttpDownloader = class<br />
public<br />
function DownloadHTTP(URL, TargetFile: string; ProgressMonitor : IProgress): Boolean;<br />
private<br />
Bytes : Integer;<br />
MaxBytes : Integer;<br />
HTTPSender: THTTPSend;<br />
ProgressMonitor : IProgress;<br />
procedure Status(Sender: TObject; Reason: THookSocketReason; const Value: String);<br />
function GetSizeFromHeader(Header: String):integer;<br />
end;<br />
<br />
implementation<br />
<br />
function THttpDownloader.DownloadHTTP(URL, TargetFile: string; ProgressMonitor : IProgress): Boolean;<br />
var<br />
HTTPGetResult: Boolean;<br />
begin<br />
Result := False;<br />
Bytes:= 0;<br />
MaxBytes:= -1;<br />
Self.ProgressMonitor:= ProgressMonitor;<br />
<br />
HTTPSender := THTTPSend.Create;<br />
try<br />
HTTPSender.Sock.OnStatus:= Status;<br />
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);<br />
if (HTTPSender.ResultCode >= 100) and (HTTPSender.ResultCode<=299) then begin<br />
HTTPSender.Document.SaveToFile(TargetFile);<br />
Result := True;<br />
end;<br />
finally<br />
HTTPSender.Free;<br />
end;<br />
end;<br />
<br />
procedure THttpDownloader.Status(Sender: TObject; Reason: THookSocketReason; const Value: String);<br />
var<br />
V, currentHeader: String;<br />
i: integer;<br />
begin<br />
//try to get filesize from headers<br />
if (MaxBytes = -1) then<br />
begin<br />
for i:= 0 to HTTPSender.Headers.Count - 1 do<br />
begin<br />
currentHeader:= HTTPSender.Headers[i];<br />
MaxBytes:= GetSizeFromHeader(currentHeader);<br />
if MaxBytes <> -1 then break;<br />
end;<br />
end;<br />
<br />
V := GetEnumName(TypeInfo(THookSocketReason), Integer(Reason)) + ' ' + Value;<br />
<br />
if Reason = THookSocketReason.HR_ReadCount then<br />
begin<br />
Bytes:= Bytes + StrToInt(Value);<br />
ProgressMonitor.ProgressNotification(V, Bytes, MaxBytes);<br />
end;<br />
end;<br />
<br />
function THttpDownloader.GetSizeFromHeader(Header: String): integer;<br />
var<br />
item : TStringList;<br />
begin<br />
Result:= -1;<br />
<br />
if Pos('Content-Length:', Header) <> 0 then<br />
begin<br />
item:= TStringList.Create();<br />
item.Delimiter:= ':';<br />
item.StrictDelimiter:=true;<br />
item.DelimitedText:=Header;<br />
if item.Count = 2 then<br />
begin<br />
Result:= StrToInt(Trim(item[1]));<br />
end;<br />
end;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
What are we doing here?<br />
<br />
First of all we look into the headers to get the file size. We have to wait and check if the header is there. The first events do not contain the Content-Length: information.<br />
<br />
Once found, we extract that information. There are several events popping up here, which you can react to. But we only check for THookSocketReason.HR_ReadCount in that example.<br />
<br />
"HR_ReadCount" provides us with the information how many bytes where read since the last event.<br />
<br />
The progress is then reported to the UI:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.ProgressNotification(Text: String; CurrentProgress: integer; MaxProgress: integer);<br />
begin<br />
if (MaxProgress <> -1) then<br />
begin<br />
ProgressBar.Max:= MaxProgress;<br />
end;<br />
<br />
ProgressBar.Position:= CurrentProgress;<br />
memoStatus.Lines.Add(Text);<br />
Application.ProcessMessages;<br />
end;<br />
</syntaxhighlight><br />
<br />
So, the final main unit will be:<br />
<br />
<syntaxhighlight lang="pascal"><br />
unit uMain;<br />
<br />
{$mode Delphi}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls, httpsend, blcksock, typinfo,<br />
uhttpdownloader;<br />
<br />
type<br />
<br />
{ TMainForm }<br />
<br />
TMainForm = class(TForm, IProgress)<br />
btnStartDownload: TButton;<br />
edtUrl: TEdit;<br />
labelUrl: TLabel;<br />
memoStatus: TMemo;<br />
ProgressBar: TProgressBar;<br />
SaveDialog: TSaveDialog;<br />
procedure btnStartDownloadClick(Sender: TObject);<br />
private<br />
{ private declarations }<br />
function GetFileNameFromURL(url: String):string;<br />
public<br />
{ public declarations }<br />
procedure ProgressNotification(Text: String; CurrentProgress : integer; MaxProgress : integer);<br />
end;<br />
<br />
var<br />
MainForm: TMainForm;<br />
<br />
implementation<br />
<br />
{$R *.lfm}<br />
<br />
{ TMainForm }<br />
<br />
procedure TMainForm.btnStartDownloadClick(Sender: TObject);<br />
var<br />
fileName: String;<br />
downloader: THttpDownloader;<br />
success: boolean;<br />
begin<br />
fileName:= GetFileNameFromURL(edtUrl.Text);<br />
SaveDialog.FileName:=fileName;<br />
if (SaveDialog.Execute) then<br />
begin<br />
memoStatus.Lines.Clear;<br />
ProgressBar.Position:=0;<br />
downloader:= THttpDownloader.Create();<br />
success:= downloader.DownloadHTTP(edtUrl.Text, SaveDialog.FileName, Self);<br />
<br />
ProgressBar.Position:=0;<br />
if Success then<br />
memoStatus.Lines.Add('Download successful')<br />
else<br />
memoStatus.Lines.Add('Error during download');<br />
<br />
end;<br />
end;<br />
<br />
function TMainForm.GetFileNameFromURL(url: String): string;<br />
var i, l : integer;<br />
fileName, current : String;<br />
begin<br />
fileName:= '';<br />
l:= Length(url);<br />
for i:= l downto 0 do begin<br />
current:= url[i];<br />
if current <> '/' then<br />
begin<br />
fileName:= current + fileName;<br />
end else begin<br />
Result:= fileName;<br />
break;<br />
end;<br />
end;<br />
end;<br />
<br />
procedure TMainForm.ProgressNotification(Text: String; CurrentProgress: integer; MaxProgress: integer);<br />
begin<br />
if (MaxProgress <> -1) then ProgressBar.Max:= MaxProgress;<br />
ProgressBar.Position:= CurrentProgress;<br />
memoStatus.Lines.Add(Text);<br />
Application.ProcessMessages;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
Reference: https://andydunkel.net/2015/09/09/lazarus_synapse_progress/<br />
<br />
==== From an HTTP server by parsing URLs: Sourceforge ====<br />
<br />
Please see [[Download from SourceForge]] for an example of downloading from sourceforge.net.<br />
<br />
=== From an HTTPS server ===<br />
<br />
This is similar to downloading from an HTTP server. In addition you need to [[Synapse#SSL.2FTLS_support|activate SSL/TLS support]] and obtain the binary file(s) for the needed library. Then you can use the same DownloadHTTP function for downloading a file from a URL starting with '''https://'''.<br />
<br />
== SSH/Telnet client sample program ==<br />
<br />
Below you will find a unit that allows you to use telnet/SSH client functionality that uses the synapse tlntsend.pas unit. An example program shows how to use this.<br />
A different, simpler way is illustrated by Leonardo Ramé at [http://leonardorame.blogspot.com/2010/01/synapse-based-ssh-client.html]. His example cannot use telnet and only sends one command, though.<br />
<br />
=== Requirements ===<br />
<br />
Apart from the Synapse sources (of which you only need a couple), if you want to use SSH functionality, you will need an encryption library that Synapse uses. If you only use Telnet, you don't need this.<br />
<br />
There are 2 choices:<br />
* Cryptlib library. Advantage: stable. Apparently able to use private keys but these are in some format that is not widely supported.<br />
* LibSSH2 library. Pascal bindings still in development, but you can use a file with your private key (in OpenSSH format) to authenticate.<br />
<br />
==== Cryptlib ====<br />
<br />
* On Windows, download a binary version of the cryptlib DLL (CL32.DLL) and put it in your source directory. If you compile to a different directory or distribute your program, you will need to distribute the DLL as well.<br />
* On Linux and OSX, install cryptlib via your package manager/other means. When distributing your application, mark cryptlib as a requirement in your .deb/.rpm/whatever package.<br />
<br />
You will also need the bindings (cryptlib.pas), present in the source distribution of cryptlib.<br />
<br />
The versions of the cryptlib binary and the bindings must match.<br />
<br />
{{Note|It seems that cryptlib is not suitable to connect to linux machines, though AIX works. Use SSH2 instead.}}<br />
<br />
==== LibSSH2 ====<br />
<br />
* On Windows, download a binary version of the libssh2 DLL (LIBSSH2.DLL) and put it in your source directory. If you compile to a different directory or distribute your program, you will need to distribute the DLL as well.<br />
* On Linux and macOS, install libssh2 via your package manager/other means. When distributing your application:<br />
** Linux: mark libssh2 as a requirement in your .deb/.rpm/whatever package.<br />
** macOS: include libssh2 in your [[Application Bundle]]'s Resources directory.<br />
<br />
You will also need ssl_libssh2.pas (see below) and the bindings: (libssh2.pas, see [http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465 this forum post]). The libssh2 binary and the bindings must match.<br />
<br />
=== Synapse libssh2 SSL plugin ===<br />
<br />
{{Note| plugin is not completed.}}<br />
<br />
<syntaxhighlight lang="pascal"><br />
{<br />
ssl_libssh2.pas version 0.2<br />
<br />
SSH2 support (draft) plugin for Synapse Library (http://www.ararat.cz/synapse) by LibSSH2 (http://libssh2.org)<br />
Requires: libssh2 pascal interface - http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465 and<br />
libssh2.dll with OpenSSL.<br />
<br />
(С) Alexey Suhinin http://x-alexey.narod.ru<br />
}<br />
<br />
{$IFDEF FPC}<br />
{$MODE DELPHI}<br />
{$ENDIF}<br />
{$H+}<br />
<br />
unit ssl_libssh2;<br />
<br />
interface<br />
<br />
uses<br />
SysUtils,<br />
blcksock, synsock,<br />
libssh2;<br />
<br />
type<br />
{:@abstract(class implementing CryptLib SSL/SSH plugin.)<br />
Instance of this class will be created for each @link(TTCPBlockSocket).<br />
You not need to create instance of this class, all is done by Synapse itself!}<br />
TSSLLibSSH2 = class(TCustomSSL)<br />
protected<br />
FSession: PLIBSSH2_SESSION;<br />
FChannel: PLIBSSH2_CHANNEL;<br />
function SSHCheck(Value: integer): Boolean;<br />
function DeInit: Boolean;<br />
public<br />
{:See @inherited}<br />
constructor Create(const Value: TTCPBlockSocket); override;<br />
destructor Destroy; override;<br />
function Connect: boolean; override;<br />
function LibName: String; override;<br />
function Shutdown: boolean; override;<br />
{:See @inherited}<br />
function BiShutdown: boolean; override;<br />
{:See @inherited}<br />
function SendBuffer(Buffer: TMemory; Len: Integer): Integer; override;<br />
{:See @inherited}<br />
function RecvBuffer(Buffer: TMemory; Len: Integer): Integer; override;<br />
{:See @inherited}<br />
function WaitingData: Integer; override;<br />
{:See @inherited}<br />
function GetSSLVersion: string; override;<br />
published<br />
end;<br />
<br />
implementation<br />
<br />
{==============================================================================}<br />
function TSSLLibSSH2.SSHCheck(Value: integer): Boolean;<br />
var<br />
PLastError: PAnsiChar;<br />
ErrMsgLen: Integer;<br />
begin<br />
Result := true;<br />
FLastError := 0;<br />
FLastErrorDesc := '';<br />
if Value<0 then<br />
begin<br />
FLastError := libssh2_session_last_error(FSession, PLastError, ErrMsglen, 0);<br />
FLastErrorDesc := PLastError;<br />
Result := false;<br />
end;<br />
end;<br />
<br />
<br />
function TSSLLibSSH2.DeInit: Boolean;<br />
begin<br />
if Assigned(FChannel) then<br />
begin<br />
libssh2_channel_free(FChannel);<br />
FChannel := nil;<br />
end;<br />
if Assigned(FSession) then<br />
begin<br />
libssh2_session_disconnect(FSession,'Goodbye');<br />
libssh2_session_free(FSession);<br />
FSession := nil;<br />
end;<br />
FSSLEnabled := False;<br />
Result := true;<br />
end;<br />
<br />
constructor TSSLLibSSH2.Create(const Value: TTCPBlockSocket);<br />
begin<br />
inherited Create(Value);<br />
FSession := nil;<br />
FChannel := nil;<br />
end;<br />
<br />
destructor TSSLLibSSH2.Destroy;<br />
begin<br />
DeInit;<br />
inherited Destroy;<br />
end;<br />
<br />
function TSSLLibSSH2.Connect: boolean;<br />
begin<br />
Result := False;<br />
if SSLEnabled then DeInit;<br />
if (FSocket.Socket <> INVALID_SOCKET) and (FSocket.SSL.SSLType = LT_SSHv2) then<br />
begin<br />
FSession := libssh2_session_init();<br />
if not Assigned(FSession) then<br />
begin<br />
FLastError := -999;<br />
FLastErrorDesc := 'Cannot initialize SSH session';<br />
exit;<br />
end;<br />
if not SSHCheck(libssh2_session_startup(FSession, FSocket.Socket)) then<br />
exit;<br />
if (FSocket.SSL.PrivateKeyFile<>'') then<br />
begin<br />
if (not SSHCheck(libssh2_userauth_publickey_fromfile(FSession, PChar(FSocket.SSL.Username), nil, PChar(FSocket.SSL.PrivateKeyFile), PChar(FSocket.SSL.KeyPassword)))) then<br />
exit;<br />
end<br />
else<br />
if (FSocket.SSL.Username<>'') and (FSocket.SSL.Password<>'') then<br />
begin<br />
if (not SSHCheck(libssh2_userauth_password(FSession, PChar(FSocket.SSL.Username), PChar(FSocket.SSL.Password)))) then<br />
exit;<br />
end;<br />
FChannel := libssh2_channel_open_session(FSession);<br />
if not assigned(FChannel) then<br />
begin<br />
SSHCheck(-1); // get error<br />
if FLastError = 0 then<br />
begin<br />
FLastError := -999; // unknown error<br />
FLastErrorDesc := 'Cannot open session';<br />
end;<br />
exit;<br />
end;<br />
if not SSHCheck(libssh2_channel_request_pty(FChannel, 'vanilla')) then<br />
exit;<br />
if not SSHCheck(libssh2_channel_shell(FChannel)) then<br />
exit;<br />
FSSLEnabled := True;<br />
Result := True;<br />
end;<br />
end;<br />
<br />
function TSSLLibSSH2.LibName: String;<br />
begin<br />
Result := 'ssl_libssh2';<br />
end;<br />
<br />
function TSSLLibSSH2.Shutdown: boolean;<br />
begin<br />
Result := DeInit;<br />
end;<br />
<br />
<br />
function TSSLLibSSH2.BiShutdown: boolean;<br />
begin<br />
Result := DeInit;<br />
end;<br />
<br />
function TSSLLibSSH2.SendBuffer(Buffer: TMemory; Len: Integer): Integer;<br />
begin<br />
Result:=libssh2_channel_write(FChannel, PChar(Buffer), Len);<br />
SSHCheck(Result);<br />
end;<br />
<br />
function TSSLLibSSH2.RecvBuffer(Buffer: TMemory; Len: Integer): Integer;<br />
begin<br />
result:=libssh2_channel_read(FChannel, PChar(Buffer), Len);<br />
SSHCheck(Result);<br />
end;<br />
<br />
function TSSLLibSSH2.WaitingData: Integer;<br />
begin<br />
if libssh2_poll_channel_read(FChannel, Result) <> 1 then Result := 0;<br />
end;<br />
<br />
function TSSLLibSSH2.GetSSLVersion: string;<br />
begin<br />
Result:=libssh2_version(0);<br />
end;<br />
<br />
initialization<br />
if libssh2_init(0)=0 then<br />
SSLImplementation := TSSLLibSSH2;<br />
<br />
finalization<br />
libssh2_exit;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
=== Terminal client class ===<br />
<br />
The telnetsshclient.pas unit below wraps around the Synapse tlntsend.pas unit and abstracts logging in, sending commands and receiving output and logging out.<br />
<br />
If you only need a telnet client and can live without SSH support, comment out {$DEFINE HAS_SSH_SUPPORT} below so you don't need to have the libssh2 dll.<br />
<br />
This unit has been lightly tested on a Linux ssh/telnet server. Additional tests welcome.<br />
<br />
<syntaxhighlight lang="pascal"><br />
unit telnetsshclient;<br />
<br />
{ Wrapper around Synapse libraries and SSL library (libssh2+libssl<br />
is used right now)<br />
Download compiled Windows dll from e.g.<br />
http://alxdm.dyndns-at-work.com:808/files/windll_libssh2.zip<br />
Download FreePascal interface files:<br />
http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465<br />
<br />
This unit allows the user to send Telnet or SSH commands and get the output<br />
Thanks to Leonardo Rame<br />
http://leonardorame.blogspot.com/2010/01/synapse-based-ssh-client.html<br />
and Ludo Brands.<br />
<br />
Written by Reinier Olislagers 2011.<br />
Modified for libssh2 by Alexey Suhinin 2012.<br />
<br />
License of code:<br />
* MIT<br />
* LGPLv2 or later (with FreePascal static linking exception)<br />
* GPLv2 or later<br />
according to your choice.<br />
Free use allowed but please don't sue or blame me.<br />
<br />
Uses other libraries/components; different licenses may apply that also can influence the combined/compiled work.<br />
}<br />
<br />
{$mode objfpc}{$H+}<br />
{$DEFINE HAS_SSH_SUPPORT} //comment out if only telnet support required<br />
{$DEFINE LIBSSH2}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils,<br />
tlntsend<br />
{$IFDEF HAS_SSH_SUPPORT}<br />
{ssl - or actually ssh - libs required by tlntsend}<br />
{$IFDEF LIBSSH2}<br />
ssl_libssh2<br />
{$ELSE}<br />
ssl_cryptlib<br />
{$ENDIF}<br />
{$ENDIF HAS_SSH_SUPPORT} ;<br />
<br />
type<br />
TProtocolType = (Telnet, SSH); //Different means of connecting<br />
TServerType = (Unix, Windows); //line endings, mostly<br />
{ TelnetSSHClient }<br />
<br />
{ TTelnetSSHClient }<br />
<br />
TTelnetSSHClient = class(TTelnetSend)<br />
protected<br />
FConnected: boolean;<br />
FOutputPosition: integer; //Keeps track of position in output stream<br />
FProtocolType: TProtocolType;<br />
FServerLineEnding: string; //depends on FServerType<br />
FServerType: TServerType;<br />
FWelcomeMessage, FTelnetLoginPrompt, FTelnetPasswordPrompt: string;<br />
procedure SetPrivateKeyFile(Value: string);<br />
function GetPrivateKeyFile: string;<br />
{ Based on protocol and servertype, set expected serverside line ending}<br />
procedure DetermineLineEnding;<br />
{ Sets port if no explicit port set. Uses protocol type: SSH or telnet}<br />
procedure DeterminePort;<br />
function GetSessionLog: string;<br />
procedure ProtocolTypeChange(Value: TProtocolType);<br />
function ReceiveData: string; //Can be used to get welcome message etc.<br />
procedure SendData(Data: string);<br />
procedure ServerTypeChange(Value: TServerType);<br />
public<br />
{All output generated during the entire session up to now}<br />
property AllOutput: string read GetSessionLog;<br />
{True if connected to server}<br />
property Connected: boolean read FConnected;<br />
{Name or IP address of host to connect to}<br />
property HostName: string read FTargetHost write FTargetHost;<br />
{Port on host used for connection. If left as 0, it will be determined by protocol type (22 for SSH, 23 for Telnet}<br />
property Port: String read FTargetPort write FTargetPort;<br />
{Location of private key file.}<br />
property PrivateKeyFile: string read GetPrivateKeyFile write SetPrivateKeyFile;<br />
{Telnet login prompt}<br />
property TelnetLoginPrompt: string read FTelnetLoginPrompt write FTelnetLoginPrompt;<br />
{Telnet password prompt}<br />
property TelnetPasswordPrompt: string read FTelnetPasswordPrompt write FTelnetPasswordPrompt;<br />
{Username used when connecting}<br />
property UserName: string read FUserName write FUserName;<br />
{Password used when connecting. Used as passphrase if PrivateKey is used}<br />
property Password: string read FPassword write FPassword;<br />
{Should we talk Telnet or SSH to the server? Defaults to SSH.}<br />
property ProtocolType: TProtocolType read FProtocolType write ProtocolTypeChange;<br />
{Windows or Unix/Linux server? Has effect on line endings. Defaults to Unix. NOTE: untested}<br />
property Servertype: TServerType read FServerType write ServerTypeChange;<br />
{Initial message displayed on logon}<br />
property WelcomeMessage: string read FWelcomeMessage;<br />
{Connect/logon to server. Requires that all authentication, protocol and hostname/port options are correct<br />
Returns descriptive result. You can then use the Connected property.}<br />
function Connect: string;<br />
{If connected, logoff from server}<br />
procedure Disconnect;<br />
{Send command to server and receive result}<br />
function CommandResult(Command: string): string; //Send command and get results<br />
constructor Create;<br />
destructor Destroy; override;<br />
end;<br />
<br />
implementation<br />
<br />
<br />
{ TelnetSSHClient }<br />
procedure TTelnetSSHClient.SetPrivateKeyFile(value: string);<br />
begin<br />
Sock.SSL.PrivateKeyFile := value;<br />
end;<br />
<br />
function TTelnetSSHClient.GetPrivateKeyFile: string;<br />
begin<br />
Result := Sock.SSL.PrivateKeyFile;<br />
end;<br />
<br />
procedure TTelnetSSHClient.DetermineLineEnding;<br />
begin<br />
case FProtocolType of<br />
SSH:<br />
begin<br />
if FServerType = Unix then<br />
FServerLineEnding := #10 //Unix<br />
else<br />
FServerLineEnding := #13 + #10; //windows<br />
end;<br />
Telnet:<br />
begin<br />
if FServerType = Unix then<br />
FServerLineEnding := #10 //Unix<br />
else<br />
FServerLineEnding := #13 + #10; //windows<br />
end;<br />
else<br />
raise Exception.Create('Unknown protocol type');<br />
end;<br />
end;<br />
<br />
procedure Ttelnetsshclient.DeterminePort;<br />
begin<br />
if FTargetPort = '' then<br />
//Set default port for protocol<br />
begin<br />
case FProtocolType of<br />
Telnet: FTargetPort := '23';<br />
SSH: FTargetPort := '22';<br />
else<br />
raise Exception.Create('Unknown protocol type.');<br />
end;<br />
<br />
end;<br />
end;<br />
<br />
procedure TTelnetSSHClient.ServerTypeChange(Value: Tservertype);<br />
begin<br />
FServerType := Value;<br />
DetermineLineEnding;<br />
end;<br />
<br />
function TTelnetSSHClient.Connect: string;<br />
var<br />
Received: string;<br />
begin<br />
result:='Unknown error while connecting';<br />
FOutputPosition := 1; //First character in output stream<br />
FWelcomeMessage := '';<br />
//Just to make sure:<br />
DetermineLineEnding;<br />
DeterminePort;<br />
if FTargetPort='0' then<br />
begin<br />
result:='Port may not be 0.';<br />
exit; //jump out of function<br />
end;<br />
case FProtocolType of<br />
Telnet:<br />
begin<br />
try<br />
if Login then<br />
begin<br />
FConnected := True;<br />
result:='Connected to telnet server.';<br />
end<br />
else<br />
if Sock.LastError<>0 then raise Exception.Create(Sock.LastErrorDesc);<br />
except<br />
on E: Exception do<br />
begin<br />
FConnected:=false;<br />
result:='Error connecting to telnet server '+FTargetHost+':'+<br />
FTargetPort+' as user ' + FUserName +<br />
'. Technical details: '+E.Message;<br />
end;<br />
end;<br />
end;<br />
SSH:<br />
begin<br />
{$IFNDEF HAS_SSH_SUPPORT}<br />
raise Exception.Create(<br />
'SSH support has not been compiled into the telnetsshclient library.');<br />
{$ENDIF HAS_SSH_SUPPORT}<br />
try<br />
if (PrivateKeyFile <> '') and (FPassword <> '') then<br />
Sock.SSL.KeyPassword:=FPassword;<br />
if SSHLogin then<br />
begin<br />
FConnected := True;<br />
result:='Connected to SSH server.';<br />
end<br />
else<br />
begin<br />
if Sock.LastError<>0 then raise Exception.Create(Sock.LastErrorDesc);<br />
if Sock.SSL.LastError<0 then raise Exception.Create(Sock.SSL.LastErrorDesc);<br />
end;<br />
except<br />
on E: Exception do<br />
begin<br />
FConnected:=false;<br />
result:='Error connecting to SSH server '+FTargetHost+':'+<br />
FTargetPort+' as user ' + FUserName +<br />
'. Technical details: '+E.Message;<br />
end;<br />
end;<br />
end;<br />
else<br />
raise Exception.Create('Unknown protocol type');<br />
end;<br />
if FConnected = True then<br />
begin<br />
FWelcomeMessage := ReceiveData;<br />
if FProtocolType=Telnet then<br />
begin<br />
//Unfortunately, we'll have to extract login ourselves<br />
//Hope it applies to all server types.<br />
if (AnsiPos(AnsiLowerCase(FTelnetLoginPrompt),AnsiLowerCase(FWelcomeMessage))>0) then<br />
begin<br />
SendData(UserName);<br />
end;<br />
Received:=ReceiveData;<br />
if (AnsiPos(AnsiLowerCase(FTelnetPasswordPrompt),AnsiLowerCase(Received))>0) then<br />
begin<br />
SendData(Password);<br />
end;<br />
//Receive additional welcome message/message of the day<br />
FWelcomeMessage:=FWelcomeMessage+LineEnding+ReceiveData;<br />
end;<br />
end;<br />
end;<br />
<br />
procedure TTelnetSSHClient.Disconnect;<br />
begin<br />
Logout;<br />
FConnected := False;<br />
end;<br />
<br />
function TTelnetSSHClient.ReceiveData: string;<br />
begin<br />
Result := '';<br />
while Sock.CanRead(1000) or (Sock.WaitingData > 0) do<br />
begin<br />
Sock.RecvPacket(1000);<br />
Result := Result + Copy(SessionLog, FOutputPosition,<br />
Length(SessionLog));<br />
FOutputPosition := Length(SessionLog) + 1;<br />
end;<br />
end;<br />
<br />
procedure Ttelnetsshclient.SendData(Data: String);<br />
begin<br />
Data := Data + FServerLineEnding; //Could be linux, could be Windows<br />
Send(Data);<br />
end;<br />
<br />
function TTelnetSSHClient.GetSessionLog: string;<br />
begin<br />
// Gets complete output up to now<br />
Result := SessionLog;<br />
end;<br />
<br />
procedure TTelnetSSHClient.ProtocolTypeChange(Value: Tprotocoltype);<br />
begin<br />
FProtocolType := Value;<br />
//Auto-determine port and line ending, if necessary<br />
DeterminePort;<br />
DetermineLineEnding;<br />
end;<br />
<br />
function TTelnetSSHClient.CommandResult(Command: string): string;<br />
begin<br />
Result := '';<br />
if FConnected then<br />
begin<br />
SendData(Command);<br />
Result := ReceiveData; //gets too much<br />
end<br />
else<br />
begin<br />
//raise exception<br />
Result := '';<br />
raise Exception.Create('Can only run command when connected');<br />
end;<br />
end;<br />
<br />
constructor TTelnetSSHClient.Create;<br />
begin<br />
inherited;<br />
FConnected := False;<br />
FProtocolType := SSH; //Could be telnet, too<br />
FServerType := Unix; //Probably a safe default.<br />
FTelnetLoginPrompt := 'login:';<br />
FTelnetPasswordPrompt := 'password:';<br />
DetermineLineEnding;<br />
DeterminePort;<br />
end;<br />
<br />
destructor TTelnetSSHClient.Destroy;<br />
begin<br />
if FConnected then<br />
Disconnect;<br />
inherited Destroy;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
=== Example client code ===<br />
<br />
To use the TTelnetSSHClient class we just made, you can use this example application, sshtest.lpr. Note that it needs to be compiled by Lazarus as it needs the LCL components to work with Synapse:<br />
<br />
<syntaxhighlight lang="pascal"><br />
program sshtest;<br />
<br />
{Test program for telnetsshclient<br />
<br />
Written by Reinier Olislagers 2011.<br />
Modified for libssh2 by Alexey Suhinin 2012.<br />
<br />
License of code:<br />
* MIT<br />
* LGPLv2 or later (with FreePascal static linking exception)<br />
* GPLv2 or later<br />
according to your choice.<br />
Free use allowed but please don't sue or blame me.<br />
<br />
Uses other libraries/components; different licenses may apply that also can influence the combined/compiled work.<br />
<br />
Run: sshtest <serverIPorhostname> [PrivateKeyFile]<br />
}<br />
{$mode objfpc}{$H+}<br />
{$APPTYPE CONSOLE}<br />
<br />
uses<br />
telnetsshclient;<br />
var<br />
comm: TTelnetSSHClient;<br />
Command: string;<br />
begin<br />
writeln('Starting.');<br />
comm:=TTelnetSSHClient.Create;<br />
comm.HostName:= ParamStr(1); //First argument on command line<br />
if comm.HostName='' then<br />
begin<br />
writeln('Please specify hostname on command line.');<br />
halt(1);<br />
end;<br />
<br />
comm.PrivateKeyFile := ParamStr(2);<br />
<br />
comm.TargetPort:='0'; //auto determine based on protocoltype<br />
comm.UserName:='root'; //change to your situation<br />
comm.Password:='password'; //change to your situation<br />
comm.ProtocolType:=SSH; //Telnet or SSH<br />
writeln(comm.Connect); //Show result of connection<br />
if comm.Connected then<br />
begin<br />
writeln('Server: ' + comm.HostName + ':'+comm.TargetPort+', user: '+comm.UserName);<br />
writeln('Welcome message:');<br />
writeln(comm.WelcomeMessage);<br />
Command:='ls -al';<br />
writeln('*** Sending ' + Command);<br />
writeln('*** Begin result****');<br />
writeln(comm.CommandResult(Command));<br />
writeln('*** End result****');<br />
writeln('');<br />
writeln('');<br />
Command:='df -h';<br />
writeln('*** Sending ' + Command);<br />
writeln('*** Begin result****');<br />
writeln(comm.CommandResult(Command));<br />
writeln('*** End result****');<br />
writeln('');<br />
writeln('');<br />
writeln('All output:');<br />
writeln('*** Begin result****');<br />
writeln(comm.AllOutput);<br />
writeln('*** End result****');<br />
comm.Disconnect;<br />
end<br />
else<br />
begin<br />
writeln('Connection to ' +<br />
comm.HostName + ':' +<br />
comm.TargetPort + ' failed.');<br />
end;<br />
comm.Free;<br />
end.<br />
</syntaxhighlight><br />
<br />
== OAuth v1/Twitter/Plurk integration ==<br />
<br />
An OAuth v1 library written in FPC that uses Synapse (and is ready for other network libraries like lNet) is available in [[fpctwit]]. FPCtwit also contains FPC Twitter and Plurk example client programs and a Lazarus Twitter client.<br />
<br />
== Other Web and Networking Articles ==<br />
<br />
* [[Portal:Web Development|Web Development Portal]]<br />
* [[Networking]]<br />
* [[Networking libraries]] - comparison of various networking libraries<br />
* [[Brook Framework]] - The perfect Free Pascal framework for your web applications. It's pure Pascal. You don't need to leave your preferred programming language.<br />
* [[Sockets]] - TCP/IP Sockets components<br />
* [[fcl-net]] - Networking library supplied with FPC<br />
* [[lNet]] - Lightweight Networking Components<br />
* [[XML Tutorial]] - XML is often utilized on network communications<br />
* [[FPC and Apache Modules]]<br />
* [[fcl-web]] - Also known as fpWeb, this is a library to develop web applications which can be deployed as cgi, fastcgi or apache modules.<br />
* [[Secure programming | Secure Programming]]<br />
* [[Internet Tools]] - A wrapper around Synapse/wininet/Android's http components simplifying https and redirections, and a XPath/XQuery/CSS Selector/JSONiq engine to process the downloaded pages<br />
<br />
== See also ==<br />
<br />
* [[Download from SourceForge]] Example that uses Synapse to download from an HTTP server that redirects.<br />
* [https://sourceforge.net/projects/visualsynapse/ Visual Synapse] component wrappers for many parts of Synapse serial and networking library (TvsComPort, TvsWebClient, TvsSniffer, TvsHTTPServer, TvsFTPServer, TvsAuthentication, TvsVisualDNS, TvsVisualHTTP, TvsVisualDUP, TvsVisualTCP, TvsVisualICMP, TvsSocksProxyInfo, TvsIPHelper, TvsSendMail and TvsSynPing).<br />
* [https://forum.lazarus.freepascal.org/index.php/topic,48677.0.html TCP/IP component based on Synapse + a small demo application]<br />
<br />
== External links ==<br />
<br />
* [http://www.ararat.cz/synapse/ Official site]<br />
* [http://synapse.ararat.cz/doc/help/ Official documentation]<br />
* [https://github.com/geby/synapse/wiki Download and Wiki]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Synapse&diff=158249Synapse2024-02-20T01:20:10Z<p>Dbannon: /* External links */</p>
<hr />
<div>{{Synapse}}<br />
<br />
Synapse provides an easy to use serial port and synchronous TCP/IP library.<br />
<br />
__TOC__<br />
<br />
== Overview ==<br />
<br />
Synapse offers [[Hardware_Access#Synaser|serial]] port and TCP/IP connectivity. It differs from other libraries that you only require to add some Synapse Pascal source code files to your code; no need for installing packages etc. The only exception is that you will need an external crypto library if you want to use encryption such as SSL/TLS/SSH. <br />
<br />
See the documentation on the official site (link below) for more details.<br />
<br />
== Installation ==<br />
<br />
Installation can be as simple as simply copying over all files to your application directory and adding the relevant Synapse units to your ''uses'' clause.<br />
<br />
A more elegant and recommended way is compiling the laz_synapse.lpk package so you can use the same units in all your projects.<br />
<br />
Synapse download/SVN info page: [http://www.ararat.cz/synapse/doku.php/download Synapse download page]<br />
<br />
== Support and bug reporting ==<br />
<br />
The Synapse project has a mailing list where support is given and patches can be submitted.<br />
<br />
Bug reports can also be mailed to the mailing list.<br />
<br />
See the [http://www.ararat.cz/synapse/doku.php/support Synapse support page]<br />
<br />
== SSL/TLS support ==<br />
<br />
You can use OpenSSL, CryptLib, StreamSecII or OpenStreamSecII SSL support with Synapse. By default, no SSL support is used. <br />
<br />
The support is activated by putting the chosen unit name in the uses section in your project. You also have to put the binary library file in your project path (Windows), or install it into your library search path (Linux, macOS, FreeBSD). <br />
<br />
Synapse loads SSL library files in runtime as dynamic libraries.<br />
<br />
* For detailed information refer to [http://www.ararat.cz/synapse/doku.php/public:howto:sslplugin SSL/TLS Plugin Architecture]<br />
* Some crypt libraries can be obtained from: http://synapse.ararat.cz/files/crypt/<br />
<br />
=== Missing library ===<br />
<br />
On Linux you need to make sure the required dynamic library is present/installed on your system. In case of cryptlib if the library is not present on the system, an error message appears during linking:<br />
<br />
/usr/bin/ld: cannot find -lcl<br />
<br />
A similar message will be displayed when using other dynamic libraries.<br />
<br />
== Web server example ==<br />
<br />
See the [[Networking#Webserver example|Webserver example]].<br />
<br />
== QOTD server query example ==<br />
<br />
See the [[QOTD|Quote of the Day server query example]]. <br />
<br />
== Sending email ==<br />
<br />
Refer to the [[Synapse - Email Examples]] article.<br />
<br />
== Downloading files ==<br />
<br />
=== From an FTP server ===<br />
<br />
Given an URL and a (path and) file name, this will download it from an FTP server.<br />
It's mostly a wrapper around the Synapse code meant to make downloading easier when handling arbitrary files.<br />
If you know exactly what you're going to download where, just a call to Synapse:<br />
<br />
<syntaxhighlight lang="pascal"><br />
FtpGetFile<br />
</syntaxhighlight><br />
<br />
will get you very far.<br />
<br />
<syntaxhighlight lang="pascal"><br />
function DownloadFTP(URL, TargetFile: string): boolean;<br />
const<br />
FTPPort=21;<br />
FTPScheme='ftp://'; //URI scheme name for FTP URLs<br />
var<br />
Host: string;<br />
Port: integer;<br />
Source: string;<br />
FoundPos: integer;<br />
begin<br />
// Strip out scheme info:<br />
if LeftStr(URL, length(FTPScheme))=FTPScheme then URL:=Copy(URL, length(FTPScheme)+1, length(URL));<br />
<br />
// Crude parsing; could have used URI parsing code in FPC packages...<br />
FoundPos:=pos('/', URL);<br />
Host:=LeftStr(URL, FoundPos-1);<br />
Source:=Copy(URL, FoundPos+1, Length(URL));<br />
<br />
//Check for port numbers:<br />
FoundPos:=pos(':', Host);<br />
Port:=FTPPort;<br />
if FoundPos>0 then<br />
begin<br />
Host:=LeftStr(Host, FoundPos-1);<br />
Port:=StrToIntDef(Copy(Host, FoundPos+1, Length(Host)),21);<br />
end;<br />
Result:=FtpGetFile(Host, IntToStr(Port), Source, TargetFile, 'anonymous', 'fpc@example.com');<br />
if result=false then writeln('DownloadFTP: error downloading '+URL+'. Details: host: '+Host+'; port: '+Inttostr(Port)+'; remote path: '+Source+' to '+TargetFile);<br />
end;<br />
</syntaxhighlight><br />
<br />
Example to get list of files in given path<br />
<br />
<syntaxhighlight lang="pascal"><br />
//Use ftpsend unit<br />
<br />
function FtpGetDir(const IP, Port, Path, User, Pass: string; DirList: TStringList): Boolean;<br />
var<br />
i: Integer;<br />
s: string;<br />
begin<br />
Result := False;<br />
with TFTPSend.Create do<br />
try<br />
Username := User;<br />
Password := Pass;<br />
TargetHost := IP;<br />
TargetPort := Port;<br />
if not Login then<br />
Exit;<br />
Result := List(Path, False);<br />
for i := 0 to FtpList.Count -1 do<br />
begin<br />
s := FTPList[i].FileName;<br />
DirList.Add(s);<br />
end;<br />
Logout;<br />
finally<br />
Free;<br />
end;<br />
end; <br />
</syntaxhighlight><br />
<br />
=== From an HTTP server ===<br />
<br />
Given an URL and a (path and) file name, this will download it from an HTTP server.<br />
Note that this code checks the HTTP status code (like 200, 404) to see if the document we got back from the server is the desired file or an error page.<br />
<br />
==== Simple version ====<br />
<br />
<syntaxhighlight lang="pascal"><br />
...<br />
uses httpsend,<br />
...<br />
function DownloadHTTP(URL, TargetFile: string): Boolean;<br />
var<br />
HTTPGetResult: Boolean;<br />
HTTPSender: THTTPSend;<br />
begin<br />
Result := False;<br />
HTTPSender := THTTPSend.Create;<br />
try<br />
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);<br />
if (HTTPSender.ResultCode >= 100) and (HTTPSender.ResultCode<=299) then begin<br />
HTTPSender.Document.SaveToFile(TargetFile);<br />
Result := True;<br />
end; <br />
finally<br />
HTTPSender.Free;<br />
end;<br />
end;</syntaxhighlight><br />
<br />
==== Advanced version ====<br />
<br />
<syntaxhighlight lang="pascal"><br />
...<br />
uses httpsend<br />
...<br />
function DownloadHTTP(URL, TargetFile: string): Boolean;<br />
// Download file; retry if necessary.<br />
// Could use Synapse HttpGetBinary, but that doesn't deal<br />
// with result codes (i.e. it happily downloads a 404 error document)<br />
const<br />
MaxRetries = 3;<br />
var<br />
HTTPGetResult: Boolean;<br />
HTTPSender: THTTPSend;<br />
RetryAttempt: Integer;<br />
begin<br />
Result := False;<br />
RetryAttempt := 1;<br />
HTTPSender := THTTPSend.Create;<br />
try<br />
try<br />
// Try to get the file<br />
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);<br />
while (HTTPGetResult = False) and (RetryAttempt < MaxRetries) do<br />
begin<br />
Sleep(500 * RetryAttempt);<br />
HTTPSender.Clear;<br />
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);<br />
RetryAttempt := RetryAttempt + 1;<br />
end;<br />
// If we have an answer from the server, check if the file<br />
// was sent to us.<br />
case HTTPSender.Resultcode of<br />
100..299:<br />
begin<br />
HTTPSender.Document.SaveToFile(TargetFile);<br />
Result := True;<br />
end; //informational, success<br />
300..399: Result := False; // redirection. Not implemented, but could be.<br />
400..499: Result := False; // client error; 404 not found etc<br />
500..599: Result := False; // internal server error<br />
else Result := False; // unknown code<br />
end;<br />
except<br />
// We don't care for the reason for this error; the download failed.<br />
Result := False;<br />
end;<br />
finally<br />
HTTPSender.Free;<br />
end;<br />
end;<br />
</syntaxhighlight><br />
<br />
==== Simple version with progress ====<br />
<br />
The following example shows how to get progress information from the HTTP download, as well as the file size.<br />
The file size is retrieved from the header information. <br />
<br />
<syntaxhighlight lang="pascal"><br />
unit uhttpdownloader;<br />
<br />
// Essential to change this. The default is {$mode objfpc}{$H+} and does not work.<br />
{$mode Delphi}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, httpsend, blcksock, typinfo;<br />
<br />
type<br />
IProgress = interface<br />
procedure ProgressNotification(Text: String; CurrentProgress : integer; MaxProgress : integer);<br />
end;<br />
<br />
type<br />
{ THttpDownloader }<br />
<br />
THttpDownloader = class<br />
public<br />
function DownloadHTTP(URL, TargetFile: string; ProgressMonitor : IProgress): Boolean;<br />
private<br />
Bytes : Integer;<br />
MaxBytes : Integer;<br />
HTTPSender: THTTPSend;<br />
ProgressMonitor : IProgress;<br />
procedure Status(Sender: TObject; Reason: THookSocketReason; const Value: String);<br />
function GetSizeFromHeader(Header: String):integer;<br />
end;<br />
<br />
implementation<br />
<br />
function THttpDownloader.DownloadHTTP(URL, TargetFile: string; ProgressMonitor : IProgress): Boolean;<br />
var<br />
HTTPGetResult: Boolean;<br />
begin<br />
Result := False;<br />
Bytes:= 0;<br />
MaxBytes:= -1;<br />
Self.ProgressMonitor:= ProgressMonitor;<br />
<br />
HTTPSender := THTTPSend.Create;<br />
try<br />
HTTPSender.Sock.OnStatus:= Status;<br />
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);<br />
if (HTTPSender.ResultCode >= 100) and (HTTPSender.ResultCode<=299) then begin<br />
HTTPSender.Document.SaveToFile(TargetFile);<br />
Result := True;<br />
end;<br />
finally<br />
HTTPSender.Free;<br />
end;<br />
end;<br />
<br />
procedure THttpDownloader.Status(Sender: TObject; Reason: THookSocketReason; const Value: String);<br />
var<br />
V, currentHeader: String;<br />
i: integer;<br />
begin<br />
//try to get filesize from headers<br />
if (MaxBytes = -1) then<br />
begin<br />
for i:= 0 to HTTPSender.Headers.Count - 1 do<br />
begin<br />
currentHeader:= HTTPSender.Headers[i];<br />
MaxBytes:= GetSizeFromHeader(currentHeader);<br />
if MaxBytes <> -1 then break;<br />
end;<br />
end;<br />
<br />
V := GetEnumName(TypeInfo(THookSocketReason), Integer(Reason)) + ' ' + Value;<br />
<br />
if Reason = THookSocketReason.HR_ReadCount then<br />
begin<br />
Bytes:= Bytes + StrToInt(Value);<br />
ProgressMonitor.ProgressNotification(V, Bytes, MaxBytes);<br />
end;<br />
end;<br />
<br />
function THttpDownloader.GetSizeFromHeader(Header: String): integer;<br />
var<br />
item : TStringList;<br />
begin<br />
Result:= -1;<br />
<br />
if Pos('Content-Length:', Header) <> 0 then<br />
begin<br />
item:= TStringList.Create();<br />
item.Delimiter:= ':';<br />
item.StrictDelimiter:=true;<br />
item.DelimitedText:=Header;<br />
if item.Count = 2 then<br />
begin<br />
Result:= StrToInt(Trim(item[1]));<br />
end;<br />
end;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
What are we doing here?<br />
<br />
First of all we look into the headers to get the file size. We have to wait and check if the header is there. The first events do not contain the Content-Length: information.<br />
<br />
Once found, we extract that information. There are several events popping up here, which you can react to. But we only check for THookSocketReason.HR_ReadCount in that example.<br />
<br />
"HR_ReadCount" provides us with the information how many bytes where read since the last event.<br />
<br />
The progress is then reported to the UI:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.ProgressNotification(Text: String; CurrentProgress: integer; MaxProgress: integer);<br />
begin<br />
if (MaxProgress <> -1) then<br />
begin<br />
ProgressBar.Max:= MaxProgress;<br />
end;<br />
<br />
ProgressBar.Position:= CurrentProgress;<br />
memoStatus.Lines.Add(Text);<br />
Application.ProcessMessages;<br />
end;<br />
</syntaxhighlight><br />
<br />
So, the final main unit will be:<br />
<br />
<syntaxhighlight lang="pascal"><br />
unit uMain;<br />
<br />
{$mode Delphi}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls, httpsend, blcksock, typinfo,<br />
uhttpdownloader;<br />
<br />
type<br />
<br />
{ TMainForm }<br />
<br />
TMainForm = class(TForm, IProgress)<br />
btnStartDownload: TButton;<br />
edtUrl: TEdit;<br />
labelUrl: TLabel;<br />
memoStatus: TMemo;<br />
ProgressBar: TProgressBar;<br />
SaveDialog: TSaveDialog;<br />
procedure btnStartDownloadClick(Sender: TObject);<br />
private<br />
{ private declarations }<br />
function GetFileNameFromURL(url: String):string;<br />
public<br />
{ public declarations }<br />
procedure ProgressNotification(Text: String; CurrentProgress : integer; MaxProgress : integer);<br />
end;<br />
<br />
var<br />
MainForm: TMainForm;<br />
<br />
implementation<br />
<br />
{$R *.lfm}<br />
<br />
{ TMainForm }<br />
<br />
procedure TMainForm.btnStartDownloadClick(Sender: TObject);<br />
var<br />
fileName: String;<br />
downloader: THttpDownloader;<br />
success: boolean;<br />
begin<br />
fileName:= GetFileNameFromURL(edtUrl.Text);<br />
SaveDialog.FileName:=fileName;<br />
if (SaveDialog.Execute) then<br />
begin<br />
memoStatus.Lines.Clear;<br />
ProgressBar.Position:=0;<br />
downloader:= THttpDownloader.Create();<br />
success:= downloader.DownloadHTTP(edtUrl.Text, SaveDialog.FileName, Self);<br />
<br />
ProgressBar.Position:=0;<br />
if Success then<br />
memoStatus.Lines.Add('Download successful')<br />
else<br />
memoStatus.Lines.Add('Error during download');<br />
<br />
end;<br />
end;<br />
<br />
function TMainForm.GetFileNameFromURL(url: String): string;<br />
var i, l : integer;<br />
fileName, current : String;<br />
begin<br />
fileName:= '';<br />
l:= Length(url);<br />
for i:= l downto 0 do begin<br />
current:= url[i];<br />
if current <> '/' then<br />
begin<br />
fileName:= current + fileName;<br />
end else begin<br />
Result:= fileName;<br />
break;<br />
end;<br />
end;<br />
end;<br />
<br />
procedure TMainForm.ProgressNotification(Text: String; CurrentProgress: integer; MaxProgress: integer);<br />
begin<br />
if (MaxProgress <> -1) then ProgressBar.Max:= MaxProgress;<br />
ProgressBar.Position:= CurrentProgress;<br />
memoStatus.Lines.Add(Text);<br />
Application.ProcessMessages;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
Reference: https://andydunkel.net/2015/09/09/lazarus_synapse_progress/<br />
<br />
==== From an HTTP server by parsing URLs: Sourceforge ====<br />
<br />
Please see [[Download from SourceForge]] for an example of downloading from sourceforge.net.<br />
<br />
=== From an HTTPS server ===<br />
<br />
This is similar to downloading from an HTTP server. In addition you need to [[Synapse#SSL.2FTLS_support|activate SSL/TLS support]] and obtain the binary file(s) for the needed library. Then you can use the same DownloadHTTP function for downloading a file from a URL starting with '''https://'''.<br />
<br />
== SSH/Telnet client sample program ==<br />
<br />
Below you will find a unit that allows you to use telnet/SSH client functionality that uses the synapse tlntsend.pas unit. An example program shows how to use this.<br />
A different, simpler way is illustrated by Leonardo Ramé at [http://leonardorame.blogspot.com/2010/01/synapse-based-ssh-client.html]. His example cannot use telnet and only sends one command, though.<br />
<br />
=== Requirements ===<br />
<br />
Apart from the Synapse sources (of which you only need a couple), if you want to use SSH functionality, you will need an encryption library that Synapse uses. If you only use Telnet, you don't need this.<br />
<br />
There are 2 choices:<br />
* Cryptlib library. Advantage: stable. Apparently able to use private keys but these are in some format that is not widely supported.<br />
* LibSSH2 library. Pascal bindings still in development, but you can use a file with your private key (in OpenSSH format) to authenticate.<br />
<br />
==== Cryptlib ====<br />
<br />
* On Windows, download a binary version of the cryptlib DLL (CL32.DLL) and put it in your source directory. If you compile to a different directory or distribute your program, you will need to distribute the DLL as well.<br />
* On Linux and OSX, install cryptlib via your package manager/other means. When distributing your application, mark cryptlib as a requirement in your .deb/.rpm/whatever package.<br />
<br />
You will also need the bindings (cryptlib.pas), present in the source distribution of cryptlib.<br />
<br />
The versions of the cryptlib binary and the bindings must match.<br />
<br />
{{Note|It seems that cryptlib is not suitable to connect to linux machines, though AIX works. Use SSH2 instead.}}<br />
<br />
==== LibSSH2 ====<br />
<br />
* On Windows, download a binary version of the libssh2 DLL (LIBSSH2.DLL) and put it in your source directory. If you compile to a different directory or distribute your program, you will need to distribute the DLL as well.<br />
* On Linux and macOS, install libssh2 via your package manager/other means. When distributing your application:<br />
** Linux: mark libssh2 as a requirement in your .deb/.rpm/whatever package.<br />
** macOS: include libssh2 in your [[Application Bundle]]'s Resources directory.<br />
<br />
You will also need ssl_libssh2.pas (see below) and the bindings: (libssh2.pas, see [http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465 this forum post]). The libssh2 binary and the bindings must match.<br />
<br />
=== Synapse libssh2 SSL plugin ===<br />
<br />
{{Note| plugin is not completed.}}<br />
<br />
<syntaxhighlight lang="pascal"><br />
{<br />
ssl_libssh2.pas version 0.2<br />
<br />
SSH2 support (draft) plugin for Synapse Library (http://www.ararat.cz/synapse) by LibSSH2 (http://libssh2.org)<br />
Requires: libssh2 pascal interface - http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465 and<br />
libssh2.dll with OpenSSL.<br />
<br />
(С) Alexey Suhinin http://x-alexey.narod.ru<br />
}<br />
<br />
{$IFDEF FPC}<br />
{$MODE DELPHI}<br />
{$ENDIF}<br />
{$H+}<br />
<br />
unit ssl_libssh2;<br />
<br />
interface<br />
<br />
uses<br />
SysUtils,<br />
blcksock, synsock,<br />
libssh2;<br />
<br />
type<br />
{:@abstract(class implementing CryptLib SSL/SSH plugin.)<br />
Instance of this class will be created for each @link(TTCPBlockSocket).<br />
You not need to create instance of this class, all is done by Synapse itself!}<br />
TSSLLibSSH2 = class(TCustomSSL)<br />
protected<br />
FSession: PLIBSSH2_SESSION;<br />
FChannel: PLIBSSH2_CHANNEL;<br />
function SSHCheck(Value: integer): Boolean;<br />
function DeInit: Boolean;<br />
public<br />
{:See @inherited}<br />
constructor Create(const Value: TTCPBlockSocket); override;<br />
destructor Destroy; override;<br />
function Connect: boolean; override;<br />
function LibName: String; override;<br />
function Shutdown: boolean; override;<br />
{:See @inherited}<br />
function BiShutdown: boolean; override;<br />
{:See @inherited}<br />
function SendBuffer(Buffer: TMemory; Len: Integer): Integer; override;<br />
{:See @inherited}<br />
function RecvBuffer(Buffer: TMemory; Len: Integer): Integer; override;<br />
{:See @inherited}<br />
function WaitingData: Integer; override;<br />
{:See @inherited}<br />
function GetSSLVersion: string; override;<br />
published<br />
end;<br />
<br />
implementation<br />
<br />
{==============================================================================}<br />
function TSSLLibSSH2.SSHCheck(Value: integer): Boolean;<br />
var<br />
PLastError: PAnsiChar;<br />
ErrMsgLen: Integer;<br />
begin<br />
Result := true;<br />
FLastError := 0;<br />
FLastErrorDesc := '';<br />
if Value<0 then<br />
begin<br />
FLastError := libssh2_session_last_error(FSession, PLastError, ErrMsglen, 0);<br />
FLastErrorDesc := PLastError;<br />
Result := false;<br />
end;<br />
end;<br />
<br />
<br />
function TSSLLibSSH2.DeInit: Boolean;<br />
begin<br />
if Assigned(FChannel) then<br />
begin<br />
libssh2_channel_free(FChannel);<br />
FChannel := nil;<br />
end;<br />
if Assigned(FSession) then<br />
begin<br />
libssh2_session_disconnect(FSession,'Goodbye');<br />
libssh2_session_free(FSession);<br />
FSession := nil;<br />
end;<br />
FSSLEnabled := False;<br />
Result := true;<br />
end;<br />
<br />
constructor TSSLLibSSH2.Create(const Value: TTCPBlockSocket);<br />
begin<br />
inherited Create(Value);<br />
FSession := nil;<br />
FChannel := nil;<br />
end;<br />
<br />
destructor TSSLLibSSH2.Destroy;<br />
begin<br />
DeInit;<br />
inherited Destroy;<br />
end;<br />
<br />
function TSSLLibSSH2.Connect: boolean;<br />
begin<br />
Result := False;<br />
if SSLEnabled then DeInit;<br />
if (FSocket.Socket <> INVALID_SOCKET) and (FSocket.SSL.SSLType = LT_SSHv2) then<br />
begin<br />
FSession := libssh2_session_init();<br />
if not Assigned(FSession) then<br />
begin<br />
FLastError := -999;<br />
FLastErrorDesc := 'Cannot initialize SSH session';<br />
exit;<br />
end;<br />
if not SSHCheck(libssh2_session_startup(FSession, FSocket.Socket)) then<br />
exit;<br />
if (FSocket.SSL.PrivateKeyFile<>'') then<br />
begin<br />
if (not SSHCheck(libssh2_userauth_publickey_fromfile(FSession, PChar(FSocket.SSL.Username), nil, PChar(FSocket.SSL.PrivateKeyFile), PChar(FSocket.SSL.KeyPassword)))) then<br />
exit;<br />
end<br />
else<br />
if (FSocket.SSL.Username<>'') and (FSocket.SSL.Password<>'') then<br />
begin<br />
if (not SSHCheck(libssh2_userauth_password(FSession, PChar(FSocket.SSL.Username), PChar(FSocket.SSL.Password)))) then<br />
exit;<br />
end;<br />
FChannel := libssh2_channel_open_session(FSession);<br />
if not assigned(FChannel) then<br />
begin<br />
SSHCheck(-1); // get error<br />
if FLastError = 0 then<br />
begin<br />
FLastError := -999; // unknown error<br />
FLastErrorDesc := 'Cannot open session';<br />
end;<br />
exit;<br />
end;<br />
if not SSHCheck(libssh2_channel_request_pty(FChannel, 'vanilla')) then<br />
exit;<br />
if not SSHCheck(libssh2_channel_shell(FChannel)) then<br />
exit;<br />
FSSLEnabled := True;<br />
Result := True;<br />
end;<br />
end;<br />
<br />
function TSSLLibSSH2.LibName: String;<br />
begin<br />
Result := 'ssl_libssh2';<br />
end;<br />
<br />
function TSSLLibSSH2.Shutdown: boolean;<br />
begin<br />
Result := DeInit;<br />
end;<br />
<br />
<br />
function TSSLLibSSH2.BiShutdown: boolean;<br />
begin<br />
Result := DeInit;<br />
end;<br />
<br />
function TSSLLibSSH2.SendBuffer(Buffer: TMemory; Len: Integer): Integer;<br />
begin<br />
Result:=libssh2_channel_write(FChannel, PChar(Buffer), Len);<br />
SSHCheck(Result);<br />
end;<br />
<br />
function TSSLLibSSH2.RecvBuffer(Buffer: TMemory; Len: Integer): Integer;<br />
begin<br />
result:=libssh2_channel_read(FChannel, PChar(Buffer), Len);<br />
SSHCheck(Result);<br />
end;<br />
<br />
function TSSLLibSSH2.WaitingData: Integer;<br />
begin<br />
if libssh2_poll_channel_read(FChannel, Result) <> 1 then Result := 0;<br />
end;<br />
<br />
function TSSLLibSSH2.GetSSLVersion: string;<br />
begin<br />
Result:=libssh2_version(0);<br />
end;<br />
<br />
initialization<br />
if libssh2_init(0)=0 then<br />
SSLImplementation := TSSLLibSSH2;<br />
<br />
finalization<br />
libssh2_exit;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
=== Terminal client class ===<br />
<br />
The telnetsshclient.pas unit below wraps around the Synapse tlntsend.pas unit and abstracts logging in, sending commands and receiving output and logging out.<br />
<br />
If you only need a telnet client and can live without SSH support, comment out {$DEFINE HAS_SSH_SUPPORT} below so you don't need to have the libssh2 dll.<br />
<br />
This unit has been lightly tested on a Linux ssh/telnet server. Additional tests welcome.<br />
<br />
<syntaxhighlight lang="pascal"><br />
unit telnetsshclient;<br />
<br />
{ Wrapper around Synapse libraries and SSL library (libssh2+libssl<br />
is used right now)<br />
Download compiled Windows dll from e.g.<br />
http://alxdm.dyndns-at-work.com:808/files/windll_libssh2.zip<br />
Download FreePascal interface files:<br />
http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465<br />
<br />
This unit allows the user to send Telnet or SSH commands and get the output<br />
Thanks to Leonardo Rame<br />
http://leonardorame.blogspot.com/2010/01/synapse-based-ssh-client.html<br />
and Ludo Brands.<br />
<br />
Written by Reinier Olislagers 2011.<br />
Modified for libssh2 by Alexey Suhinin 2012.<br />
<br />
License of code:<br />
* MIT<br />
* LGPLv2 or later (with FreePascal static linking exception)<br />
* GPLv2 or later<br />
according to your choice.<br />
Free use allowed but please don't sue or blame me.<br />
<br />
Uses other libraries/components; different licenses may apply that also can influence the combined/compiled work.<br />
}<br />
<br />
{$mode objfpc}{$H+}<br />
{$DEFINE HAS_SSH_SUPPORT} //comment out if only telnet support required<br />
{$DEFINE LIBSSH2}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils,<br />
tlntsend<br />
{$IFDEF HAS_SSH_SUPPORT}<br />
{ssl - or actually ssh - libs required by tlntsend}<br />
{$IFDEF LIBSSH2}<br />
ssl_libssh2<br />
{$ELSE}<br />
ssl_cryptlib<br />
{$ENDIF}<br />
{$ENDIF HAS_SSH_SUPPORT} ;<br />
<br />
type<br />
TProtocolType = (Telnet, SSH); //Different means of connecting<br />
TServerType = (Unix, Windows); //line endings, mostly<br />
{ TelnetSSHClient }<br />
<br />
{ TTelnetSSHClient }<br />
<br />
TTelnetSSHClient = class(TTelnetSend)<br />
protected<br />
FConnected: boolean;<br />
FOutputPosition: integer; //Keeps track of position in output stream<br />
FProtocolType: TProtocolType;<br />
FServerLineEnding: string; //depends on FServerType<br />
FServerType: TServerType;<br />
FWelcomeMessage, FTelnetLoginPrompt, FTelnetPasswordPrompt: string;<br />
procedure SetPrivateKeyFile(Value: string);<br />
function GetPrivateKeyFile: string;<br />
{ Based on protocol and servertype, set expected serverside line ending}<br />
procedure DetermineLineEnding;<br />
{ Sets port if no explicit port set. Uses protocol type: SSH or telnet}<br />
procedure DeterminePort;<br />
function GetSessionLog: string;<br />
procedure ProtocolTypeChange(Value: TProtocolType);<br />
function ReceiveData: string; //Can be used to get welcome message etc.<br />
procedure SendData(Data: string);<br />
procedure ServerTypeChange(Value: TServerType);<br />
public<br />
{All output generated during the entire session up to now}<br />
property AllOutput: string read GetSessionLog;<br />
{True if connected to server}<br />
property Connected: boolean read FConnected;<br />
{Name or IP address of host to connect to}<br />
property HostName: string read FTargetHost write FTargetHost;<br />
{Port on host used for connection. If left as 0, it will be determined by protocol type (22 for SSH, 23 for Telnet}<br />
property Port: String read FTargetPort write FTargetPort;<br />
{Location of private key file.}<br />
property PrivateKeyFile: string read GetPrivateKeyFile write SetPrivateKeyFile;<br />
{Telnet login prompt}<br />
property TelnetLoginPrompt: string read FTelnetLoginPrompt write FTelnetLoginPrompt;<br />
{Telnet password prompt}<br />
property TelnetPasswordPrompt: string read FTelnetPasswordPrompt write FTelnetPasswordPrompt;<br />
{Username used when connecting}<br />
property UserName: string read FUserName write FUserName;<br />
{Password used when connecting. Used as passphrase if PrivateKey is used}<br />
property Password: string read FPassword write FPassword;<br />
{Should we talk Telnet or SSH to the server? Defaults to SSH.}<br />
property ProtocolType: TProtocolType read FProtocolType write ProtocolTypeChange;<br />
{Windows or Unix/Linux server? Has effect on line endings. Defaults to Unix. NOTE: untested}<br />
property Servertype: TServerType read FServerType write ServerTypeChange;<br />
{Initial message displayed on logon}<br />
property WelcomeMessage: string read FWelcomeMessage;<br />
{Connect/logon to server. Requires that all authentication, protocol and hostname/port options are correct<br />
Returns descriptive result. You can then use the Connected property.}<br />
function Connect: string;<br />
{If connected, logoff from server}<br />
procedure Disconnect;<br />
{Send command to server and receive result}<br />
function CommandResult(Command: string): string; //Send command and get results<br />
constructor Create;<br />
destructor Destroy; override;<br />
end;<br />
<br />
implementation<br />
<br />
<br />
{ TelnetSSHClient }<br />
procedure TTelnetSSHClient.SetPrivateKeyFile(value: string);<br />
begin<br />
Sock.SSL.PrivateKeyFile := value;<br />
end;<br />
<br />
function TTelnetSSHClient.GetPrivateKeyFile: string;<br />
begin<br />
Result := Sock.SSL.PrivateKeyFile;<br />
end;<br />
<br />
procedure TTelnetSSHClient.DetermineLineEnding;<br />
begin<br />
case FProtocolType of<br />
SSH:<br />
begin<br />
if FServerType = Unix then<br />
FServerLineEnding := #10 //Unix<br />
else<br />
FServerLineEnding := #13 + #10; //windows<br />
end;<br />
Telnet:<br />
begin<br />
if FServerType = Unix then<br />
FServerLineEnding := #10 //Unix<br />
else<br />
FServerLineEnding := #13 + #10; //windows<br />
end;<br />
else<br />
raise Exception.Create('Unknown protocol type');<br />
end;<br />
end;<br />
<br />
procedure Ttelnetsshclient.DeterminePort;<br />
begin<br />
if FTargetPort = '' then<br />
//Set default port for protocol<br />
begin<br />
case FProtocolType of<br />
Telnet: FTargetPort := '23';<br />
SSH: FTargetPort := '22';<br />
else<br />
raise Exception.Create('Unknown protocol type.');<br />
end;<br />
<br />
end;<br />
end;<br />
<br />
procedure TTelnetSSHClient.ServerTypeChange(Value: Tservertype);<br />
begin<br />
FServerType := Value;<br />
DetermineLineEnding;<br />
end;<br />
<br />
function TTelnetSSHClient.Connect: string;<br />
var<br />
Received: string;<br />
begin<br />
result:='Unknown error while connecting';<br />
FOutputPosition := 1; //First character in output stream<br />
FWelcomeMessage := '';<br />
//Just to make sure:<br />
DetermineLineEnding;<br />
DeterminePort;<br />
if FTargetPort='0' then<br />
begin<br />
result:='Port may not be 0.';<br />
exit; //jump out of function<br />
end;<br />
case FProtocolType of<br />
Telnet:<br />
begin<br />
try<br />
if Login then<br />
begin<br />
FConnected := True;<br />
result:='Connected to telnet server.';<br />
end<br />
else<br />
if Sock.LastError<>0 then raise Exception.Create(Sock.LastErrorDesc);<br />
except<br />
on E: Exception do<br />
begin<br />
FConnected:=false;<br />
result:='Error connecting to telnet server '+FTargetHost+':'+<br />
FTargetPort+' as user ' + FUserName +<br />
'. Technical details: '+E.Message;<br />
end;<br />
end;<br />
end;<br />
SSH:<br />
begin<br />
{$IFNDEF HAS_SSH_SUPPORT}<br />
raise Exception.Create(<br />
'SSH support has not been compiled into the telnetsshclient library.');<br />
{$ENDIF HAS_SSH_SUPPORT}<br />
try<br />
if (PrivateKeyFile <> '') and (FPassword <> '') then<br />
Sock.SSL.KeyPassword:=FPassword;<br />
if SSHLogin then<br />
begin<br />
FConnected := True;<br />
result:='Connected to SSH server.';<br />
end<br />
else<br />
begin<br />
if Sock.LastError<>0 then raise Exception.Create(Sock.LastErrorDesc);<br />
if Sock.SSL.LastError<0 then raise Exception.Create(Sock.SSL.LastErrorDesc);<br />
end;<br />
except<br />
on E: Exception do<br />
begin<br />
FConnected:=false;<br />
result:='Error connecting to SSH server '+FTargetHost+':'+<br />
FTargetPort+' as user ' + FUserName +<br />
'. Technical details: '+E.Message;<br />
end;<br />
end;<br />
end;<br />
else<br />
raise Exception.Create('Unknown protocol type');<br />
end;<br />
if FConnected = True then<br />
begin<br />
FWelcomeMessage := ReceiveData;<br />
if FProtocolType=Telnet then<br />
begin<br />
//Unfortunately, we'll have to extract login ourselves<br />
//Hope it applies to all server types.<br />
if (AnsiPos(AnsiLowerCase(FTelnetLoginPrompt),AnsiLowerCase(FWelcomeMessage))>0) then<br />
begin<br />
SendData(UserName);<br />
end;<br />
Received:=ReceiveData;<br />
if (AnsiPos(AnsiLowerCase(FTelnetPasswordPrompt),AnsiLowerCase(Received))>0) then<br />
begin<br />
SendData(Password);<br />
end;<br />
//Receive additional welcome message/message of the day<br />
FWelcomeMessage:=FWelcomeMessage+LineEnding+ReceiveData;<br />
end;<br />
end;<br />
end;<br />
<br />
procedure TTelnetSSHClient.Disconnect;<br />
begin<br />
Logout;<br />
FConnected := False;<br />
end;<br />
<br />
function TTelnetSSHClient.ReceiveData: string;<br />
begin<br />
Result := '';<br />
while Sock.CanRead(1000) or (Sock.WaitingData > 0) do<br />
begin<br />
Sock.RecvPacket(1000);<br />
Result := Result + Copy(SessionLog, FOutputPosition,<br />
Length(SessionLog));<br />
FOutputPosition := Length(SessionLog) + 1;<br />
end;<br />
end;<br />
<br />
procedure Ttelnetsshclient.SendData(Data: String);<br />
begin<br />
Data := Data + FServerLineEnding; //Could be linux, could be Windows<br />
Send(Data);<br />
end;<br />
<br />
function TTelnetSSHClient.GetSessionLog: string;<br />
begin<br />
// Gets complete output up to now<br />
Result := SessionLog;<br />
end;<br />
<br />
procedure TTelnetSSHClient.ProtocolTypeChange(Value: Tprotocoltype);<br />
begin<br />
FProtocolType := Value;<br />
//Auto-determine port and line ending, if necessary<br />
DeterminePort;<br />
DetermineLineEnding;<br />
end;<br />
<br />
function TTelnetSSHClient.CommandResult(Command: string): string;<br />
begin<br />
Result := '';<br />
if FConnected then<br />
begin<br />
SendData(Command);<br />
Result := ReceiveData; //gets too much<br />
end<br />
else<br />
begin<br />
//raise exception<br />
Result := '';<br />
raise Exception.Create('Can only run command when connected');<br />
end;<br />
end;<br />
<br />
constructor TTelnetSSHClient.Create;<br />
begin<br />
inherited;<br />
FConnected := False;<br />
FProtocolType := SSH; //Could be telnet, too<br />
FServerType := Unix; //Probably a safe default.<br />
FTelnetLoginPrompt := 'login:';<br />
FTelnetPasswordPrompt := 'password:';<br />
DetermineLineEnding;<br />
DeterminePort;<br />
end;<br />
<br />
destructor TTelnetSSHClient.Destroy;<br />
begin<br />
if FConnected then<br />
Disconnect;<br />
inherited Destroy;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
=== Example client code ===<br />
<br />
To use the TTelnetSSHClient class we just made, you can use this example application, sshtest.lpr. Note that it needs to be compiled by Lazarus as it needs the LCL components to work with Synapse:<br />
<br />
<syntaxhighlight lang="pascal"><br />
program sshtest;<br />
<br />
{Test program for telnetsshclient<br />
<br />
Written by Reinier Olislagers 2011.<br />
Modified for libssh2 by Alexey Suhinin 2012.<br />
<br />
License of code:<br />
* MIT<br />
* LGPLv2 or later (with FreePascal static linking exception)<br />
* GPLv2 or later<br />
according to your choice.<br />
Free use allowed but please don't sue or blame me.<br />
<br />
Uses other libraries/components; different licenses may apply that also can influence the combined/compiled work.<br />
<br />
Run: sshtest <serverIPorhostname> [PrivateKeyFile]<br />
}<br />
{$mode objfpc}{$H+}<br />
{$APPTYPE CONSOLE}<br />
<br />
uses<br />
telnetsshclient;<br />
var<br />
comm: TTelnetSSHClient;<br />
Command: string;<br />
begin<br />
writeln('Starting.');<br />
comm:=TTelnetSSHClient.Create;<br />
comm.HostName:= ParamStr(1); //First argument on command line<br />
if comm.HostName='' then<br />
begin<br />
writeln('Please specify hostname on command line.');<br />
halt(1);<br />
end;<br />
<br />
comm.PrivateKeyFile := ParamStr(2);<br />
<br />
comm.TargetPort:='0'; //auto determine based on protocoltype<br />
comm.UserName:='root'; //change to your situation<br />
comm.Password:='password'; //change to your situation<br />
comm.ProtocolType:=SSH; //Telnet or SSH<br />
writeln(comm.Connect); //Show result of connection<br />
if comm.Connected then<br />
begin<br />
writeln('Server: ' + comm.HostName + ':'+comm.TargetPort+', user: '+comm.UserName);<br />
writeln('Welcome message:');<br />
writeln(comm.WelcomeMessage);<br />
Command:='ls -al';<br />
writeln('*** Sending ' + Command);<br />
writeln('*** Begin result****');<br />
writeln(comm.CommandResult(Command));<br />
writeln('*** End result****');<br />
writeln('');<br />
writeln('');<br />
Command:='df -h';<br />
writeln('*** Sending ' + Command);<br />
writeln('*** Begin result****');<br />
writeln(comm.CommandResult(Command));<br />
writeln('*** End result****');<br />
writeln('');<br />
writeln('');<br />
writeln('All output:');<br />
writeln('*** Begin result****');<br />
writeln(comm.AllOutput);<br />
writeln('*** End result****');<br />
comm.Disconnect;<br />
end<br />
else<br />
begin<br />
writeln('Connection to ' +<br />
comm.HostName + ':' +<br />
comm.TargetPort + ' failed.');<br />
end;<br />
comm.Free;<br />
end.<br />
</syntaxhighlight><br />
<br />
== OAuth v1/Twitter/Plurk integration ==<br />
<br />
An OAuth v1 library written in FPC that uses Synapse (and is ready for other network libraries like lNet) is available in [[fpctwit]]. FPCtwit also contains FPC Twitter and Plurk example client programs and a Lazarus Twitter client.<br />
<br />
== Other Web and Networking Articles ==<br />
<br />
* [[Portal:Web Development|Web Development Portal]]<br />
* [[Networking]]<br />
* [[Networking libraries]] - comparison of various networking libraries<br />
* [[Brook Framework]] - The perfect Free Pascal framework for your web applications. It's pure Pascal. You don't need to leave your preferred programming language.<br />
* [[Sockets]] - TCP/IP Sockets components<br />
* [[fcl-net]] - Networking library supplied with FPC<br />
* [[lNet]] - Lightweight Networking Components<br />
* [[XML Tutorial]] - XML is often utilized on network communications<br />
* [[FPC and Apache Modules]]<br />
* [[fcl-web]] - Also known as fpWeb, this is a library to develop web applications which can be deployed as cgi, fastcgi or apache modules.<br />
* [[Secure programming | Secure Programming]]<br />
* [[Internet Tools]] - A wrapper around Synapse/wininet/Android's http components simplifying https and redirections, and a XPath/XQuery/CSS Selector/JSONiq engine to process the downloaded pages<br />
<br />
== See also ==<br />
<br />
* [[Download from SourceForge]] Example that uses Synapse to download from an HTTP server that redirects.<br />
* [https://sourceforge.net/projects/visualsynapse/ Visual Synapse] component wrappers for many parts of Synapse serial and networking library (TvsComPort, TvsWebClient, TvsSniffer, TvsHTTPServer, TvsFTPServer, TvsAuthentication, TvsVisualDNS, TvsVisualHTTP, TvsVisualDUP, TvsVisualTCP, TvsVisualICMP, TvsSocksProxyInfo, TvsIPHelper, TvsSendMail and TvsSynPing).<br />
* [https://forum.lazarus.freepascal.org/index.php/topic,48677.0.html TCP/IP component based on Synapse + a small demo application]<br />
<br />
== External links ==<br />
<br />
* [http://www.ararat.cz/synapse/ Official site]<br />
* [http://synapse.ararat.cz/doc/help/ Official documentation]<br />
* [https://github.com/geby/synapse/wiki Download and Wiki]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=KControls/KMemo_notes&diff=158164KControls/KMemo notes2024-01-29T06:56:13Z<p>Dbannon: update bullets code</p>
<hr />
<div>= Introduction =<br />
<br />
This page is about the KMemo Component, a part of [[KControls]]. KMemo provides a Cross Platform (Linux, Windows, macOS) memo capable of a range of text font styles, colours and similar. Its also capable of showing images and a range of other things but that may need to be dealt with at a later stage. And, importantly, it really does work pretty much the same way across Linux, Windows and Mac. Possibly other platforms as well but I cannot speak authoritatively there.<br />
<br />
The content presented here is in addition to the manual distributed with the KControls package. Its written by a KMemo user, not the author and could contain errors, omissions and possibly outright lies ! Someone else will undoubtedly use a different set of methods so that person is urged to document what they find too. And correct any errors they find here.<br />
<br />
= Underlying structure =<br />
<br />
Its important, when using KMemo, to understand how the memo content and its meta data is stored. Everything is in a Block, and KMemo itself has a Blocks property. A Block has a Text property that contains the text being displayed, a Font property and a number of others controlling how the text is displayed. Every change to font style, colour, size and so on requires a new block. Block classes discussed here include TKMemoTextBlock, TKMemoParagraph, TKMemoHyperlink. A TKMemoParagraph appears between two paragraphs.<br />
<br />
This '''line of ''text is an''' example.'' <br />
<br />
It will contain 5 blocks. <br />
* Block 0 "This " - normal font.<br />
* Block 1 "line of " - bold<br />
* Block 2 "text is an " - bold and italic<br />
* Block 3 "example." - italic<br />
* Block 4 - a paragraph marker.<br />
<br />
The KMemo, Block and Blocks classes have a large number of other Properties and Methods, far too many to possibly document here. And there are also many associated classes. Lazarus will prompt you with them and the names are reasonably intuitive.<br />
<br />
=Inserting Text=<br />
<br />
Easy. KMemo1.Blocks.AddTextBlock(AString). That will append the block at the end of any existing blocks. You can add a number after the string parameter and it will be inserted at that BlockIndex. Its a function, returning the TKMemoBlock so, once created you can alter how that text is presented.<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure Form1.AddText();<br />
var<br />
TB: TKMemoTextBlock;<br />
begin<br />
TB := KMemo1.Blocks.AddTextBlock(InStr);<br />
TB.TextStyle.Font.Size := 16;<br />
TB.TextStyle.Font.Style := TB.TextStyle.Font.Style + [fsBold];<br />
end;</syntaxhighlight><br />
<br />
Now, presumably, AddTextBlock() has allocated some memory for that TextBlock, we assume it will Free it when appropriate.<br />
<br />
If you wish to insert some text at a particular spot that will take on the font characteristics of the insertion point, its even easier. Several functions will return a SelIndex (its just a count of characters starting at zero) -<br />
<br />
<syntaxhighlight lang="pascal">KMemo1.Blocks.InsertPlainText(SelIndex, 'some bit of text to insert');</syntaxhighlight><br />
<br />
=Selections=<br />
<br />
... On our selection ... (sorry, Aussie joke)<br />
<br />
Kmemo offers a couple of functions, KMemo1.Blocks.RealSelStart, KMemo1.Blocks.RealSelEnd that return (read only) SelectionIndexes. ~End is always greater (or equal to) than ~Start unlike KMemo1.SelStart, Kmemo1.SelEnd which are sensitive to the direction the user wiped the mouse. KMemo1.Blocks.RealSelStart tracks the cursor position while a user is typing.<br />
<br />
The function KMemo1..Blocks.SelText returns the selected text (if any) irrespective of intervening block structure. Note that it might include newline characters.<br />
<br />
=Miscellaneous=<br />
<br />
NOTE: By default upon creation of a table, a text block containing a new line is created within it. Usually it has to be removed by:<br />
<br />
<syntaxhighlight lang="pascal">procedure Form1.AddText();<br />
KMemo1.Blocks.Clear;end;</syntaxhighlight><br />
<br />
'''WARNING:''' TKmemo does '''not''' support RightToLeft languages.<br />
<br />
=Playing With Blocks=<br />
<br />
You will get used to working with the two indexes, one (utf8) character based and the other Block based. Both start at zero.<br />
<br />
The Blocks class has a property, Items that can let you address an individual block, so KMemo1.Blocks.Items[BlockNo] is a particular block. And it has a whole bunch of properties and methods.<br />
<br />
There are KMemo1.Blocks.Count blocks in a KMemo. And there is length(KMemo1.Blocks.Text) characters but only in a Unix based system such as Linux or macOS. All systems allow one character for a Paragraph marker in the KMemo itself but Windows, being Windows puts a CR/LF, (#13#10) at the end of each paragraph in KMemo1.Blocks.Text. So you need allow for that, for an example, see below about Searching.<br />
<br />
'''How to convert between the two ? Like this'''<br />
<br />
<syntaxhighlight lang="pascal">var <br />
BlockNo, CharNo, LocalIndex : longint;<br />
begin<br />
CharNo := SomeValue;<br />
BlockNo := Kmemo1.Blocks.IndexToBlockIndex(CharNo, LocalIndex);<br />
</syntaxhighlight> <br />
<br />
BlockNo now has what block number CharNo points to and LocalIndex tells us how far in the block we are. Useful information, if LocalIndex is 0, we are at the start of a block, if its length(KMemo1.Blocks.Items[BlockNo].Text)-1 we must be at the end of a block. Dont forget that the Text component of a Block is, effectively, a Pascal string and it starts at 1, not zero. So, the first character on the first line of a KMemo is<br />
<br />
* KMemo.Blocks.Items[0].Text[1] <br />
* TKMemoSelectionIndex = 0<br />
* and Kmemo1.Blocks.IndexToBlockIndex(0, LocalIndex); // will return (block) 0 and set LocalIndex to 0<br />
<br />
'''The reverse''' -<br />
<br />
<syntaxhighlight lang="pascal">CharNo := KMemo1.Blocks.BlockToIndex(KMemo1.Blocks.Items[BlockNo]); </syntaxhighlight><br />
<br />
will convert a blocknumber to SelectionIndex type of number. A SelectionIndex is a 0 based count of UTF8 characters, not bytes.<br />
<br />
When iterating over blocks, important to know what each block is -<br />
<br />
<syntaxhighlight lang="pascal">if KMemo1.Blocks.Items[BlockNo].ClassNameIs('TKMemoHyperlink') then<br />
URL := TKMemoHyperlink(KMemo1.Blocks.Items[BlockNo].URL);</syntaxhighlight><br />
<br />
Here we have found out that the Block at BlockNo is a Hyperlink block, we can therefore cast it to TKMemoHyperlink and get the URL stored there previously.<br />
<br />
<syntaxhighlight lang="pascal">Kmemo1.Blocks.Delete(BlockNo);</syntaxhighlight> <br />
<br />
To delete a block. The source says parameter is an integer, not a TKMemoBlockIndex so you may miss it when looking for something to delete a block. But that appears to be the one to use ! <br />
<br />
<syntaxhighlight lang="pascal">KMemo1.Blocks.DeleteChar(Index);</syntaxhighlight> <br />
<br />
Can also be a bit confusing. You would expect it to delete the character pointed to by Index, and yes, it might. But if there is another area of Text selected when you call it, instead, it will delete that other area of text. And that can be quite a surprise ! So, something like:<br />
<br />
<syntaxhighlight lang="pascal"><br />
KMemo1.Select(Index, 0);<br />
while Cnt < Len do begin <br />
KMemo1.Blocks.DeleteChar(Index); <br />
inc(Cnt);<br />
end;<br />
</syntaxhighlight><br />
<br />
And, you may need to restore the selection points afterwards.<br />
<br />
As a paragraph may consist of many blocks and one para block, its useful to know where that para block is. KMemo1.NearestParagraphIndex returns the index of the FOLLOWING para block (obviously, not necessarily the 'nearest').<br />
<br />
=Hyperlinks=<br />
<br />
TKMemo supports text-only hyperlinks. As of October 2017 Hyperlinks containing images are not supported.<br />
<br />
OK, lets suppose you have a KMemo full of text and you want to make a word of it a hyperlink. It will be blue, have an underline and when clicked, do something. The hyperlink will be one block (remember, everything is a block), so if the text we want to convert is already a discrete block, easy, delete it and then insert a new hyperlink block with the same text. However, its more likely we need to split a block. The process is to delete the characters that we want to become the link, split the block at that spot, create a TKMemoHyperlink block, configure it and insert it between the split blocks. <br />
<br />
This procedure will make a hyperlink at the character position, Index and it will be Len long. It exits doing nothing if that spot is already a hyperlink.<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.MakeDemoLink(Index, Len : longint);<br />
var<br />
Hyperlink: TKMemoHyperlink;<br />
BlockNo, Offset : longint;<br />
DontSplit : Boolean = false;<br />
Link : ANSIString;<br />
begin<br />
// Is it already a Hyperlink ?<br />
BlockNo := KMemo1.Blocks.IndexToBlockIndex(Index, Offset);<br />
if KMemo1.Blocks.Items[BlockNo].ClassNameIs('TKHyperlink') then exit();<br />
Link := copy(KMemo1.Blocks.Items[BlockNo].Text, Offset+1, Len);<br />
if length(Kmemo1.Blocks.Items[BlockNo].Text) = Len then DontSplit := True;<br />
TheKMemo.SelStart := Index; <br />
TheKMemo.SelLength := Len;<br />
TheKMemo.ClearSelection();<br />
if not DontSplit then BlockNo := KMemo1.SplitAt(Index);<br />
Hyperlink := TKMemoHyperlink.Create;<br />
Hyperlink.Text := Link;<br />
Hyperlink.OnClick := @OnUserClickLink;<br />
KMemo1.Blocks.AddHyperlink(Hyperlink, BlockNo);<br />
end;<br />
</syntaxhighlight> <br />
<br />
Now, danger Will Robertson ! This function will fail if you pass it parameters of text that are not within one block. But only because of the "Link := copy(..." line. Easy to fix with a few extra lines of code.<br />
<br />
And you may need to first record the existing selection and restore it afterwards too.<br />
<br />
Oh, and whats this "@OnUserClickLink" ? Its the address of a procedure "OnUserClickLink()" that might be defined like this -<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TEditBoxForm.OnUserClickLink(sender : TObject);<br />
begin<br />
showmessage('Wow, someone clicked ' + TKMemoHyperlink(Sender).Text);<br />
end;<br />
</syntaxhighlight><br />
<br />
=Search=<br />
<br />
Despite all those methods, I could not find a Search function. But if your application is just text based (ie no images or nested containers) its not that difficult to search KMemo1.Blocks.Text, treat it as an ANSIString. On Unix like systems, the position in KMemo1.Blocks.Text can be used directly as a TKMemoSelectionIndex. Sadly, in Windows, KMemo1.Blocks.Text has Window's two character line endings, but in KMemo itself, only one char is reserved for line endings. So, count the #13 characters from the start to the position of interest and subtract that from what becomes our TKMemoSelection index. <br />
<br />
This UTF8 compliant function will return 0 or the TKMemoSelectionIndex of the searched for term. It depends on the LazUTF8 unit so add it to your uses clause. It can be called repeatably, passing the previously found index plus 1 each time to find multiple incidences of a term. Set MoveCursor to True and it will show the user where the searched for term is. You will note most of the code is about fixing up the Windows problem.<br />
<br />
<syntaxhighlight lang="pascal"><br />
function TForm1.Search(Term : ANSIString; StartAt : longint = 1; MoveCursor : Boolean = False) : longint;<br />
var<br />
Ptr, EndP : pchar;<br />
Offset : longint;<br />
NumbCR : longint;<br />
begin<br />
Result := UTF8Pos(Term, KMemo1.Blocks.text, StartAt);<br />
{$IFDEF WINDOWS} // Sadley we need to subtract the extra CR windows adds to a newline<br />
if Result = 0 then exit();<br />
NumbCR := 0;<br />
Ptr := PChar(KMemo1.Blocks.text);<br />
EndP := Ptr + Result-1;<br />
while Ptr < EndP do begin<br />
if Ptr^ = #13 then inc(NumbCR);<br />
inc(Ptr);<br />
end;<br />
Result := Result - NumbCR;<br />
{$ENDIF} // does no harm in Unix but adds 66mS with my test note.<br />
if MoveCursor then begin<br />
KMemo1.SelStart := Result;<br />
KMemo1.SelEnd := Result;<br />
KMemo1.SetFocus;<br />
end;<br />
end; <br />
</syntaxhighlight><br />
<br />
=Bullets=<br />
<br />
Bullets are a popular way to display a series of (maybe) short items. KMemo does have an "out of the box" bullet capability but not every part of it works exactly as people generally expect. The underlying issue is that KMemo records the "bulletness" of a bit of text in the paragraph marker at the end of the text. This results in some unexpected (to the end user) behaviour, particularly when using backspace key at beginning or end of a line of bullet text -<br />
* If the user backspaces from immediately after a bullet, perhaps hoping to delete the last character there, instead, they will delete the paragraph marker (not surprising) and remove the bullet attribute from that line of text (thats is surprising to end user).<br />
* If the user places the cursor at start of a bullet text and presses backspace, expecting to cancel the "bulletness", instead, it acts as a backspace should and removes the leading paragraph marked and merges the line with the line above. And if the line above was not already a bullet, it now becomes one !<br />
<br />
There are a number of subcases of the above. Each has to be dealt with. The solution at the moment is to intercept the backspace key and, if the cursor is in one of the critical positions with respect to a bullet, prevent it being passed on and doing what is expected there and then. This works but but is complicated.<br />
<br />
Firstly, in KMemo1KeyDown() we look at every keystroke, we have to anyway, will need to intercept any one of the ctrl or similar key strokes for other reasons. But we are very interested in a Backspace -<br />
<syntaxhighlight lang="pascal"><br />
procedure TEditBoxForm.KMemo1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);<br />
begin<br />
// do all sorts of stuff and exit early if not a Backspace<br />
if BackSpaceBullet() then <br />
Key := 0; // eat that key because it was used in BackSpaceBullet()<br />
// if BackspaceBullet() ret false, let the Backspace go through to the KMemo<br />
end;<br />
</syntaxhighlight><br />
<br />
OK, now BackspaceBullet() will check to see if the cursor is in a position near a bullet that needs our attention.<br />
<syntaxhighlight lang="pascal"><br />
function TEditBoxForm.BackSpaceBullet() : boolean;<br />
var<br />
BlockNo, PosInBlock : longint;<br />
ParaBlockNo : integer = -1;<br />
IsBulletPara : boolean = false; // The para the cursor is in.<br />
begin<br />
Result := false;<br />
ParaBlockNo := Kmemo1.NearestParagraphIndex; // Thats para blockno. that controls where cursor is.<br />
if ParaBlockNo < 2 then exit; // Don't mess with title.<br />
BlockNo := kmemo1.Blocks.IndexToBlockIndex(KMemo1.RealSelStart, PosInBlock); // Block with cursor<br />
if PosInBlock > 0 then exit; // not the sort of BS we are looking for.<br />
// ======== BS when on first char of bullet line.========<br />
if TKMemoParagraph(KMemo1.Blocks[ParaBlockNo]).Numbering <> pnuNone then begin // ie, this is a bullet para.<br />
IsBulletPara := True;<br />
if kmemo1.Blocks.Items[BlockNo-1].ClassNameIs('TkMemoParagraph') then begin // Cursor is on first char of a bullet para.<br />
SetBullet(TKMemoParagraph(KMemo1.Blocks[ParaBlockNo]), False);<br />
exit(True); // My work here is done.<br />
end;<br />
end;<br />
// If previous para is already a bullet and current one is not, we set current one to same<br />
// bullet level as previous and allow BS to do the rest. BlockNo would have been set above<br />
// and we know we are on the first char of the paragraph.<br />
if (BlockNo > 0) and (not IsBulletPara) then begin // Cursor is on first char of a non-bullet para.<br />
if kmemo1.Blocks.Items[BlockNo-1].ClassNameIs('TkMemoParagraph') // previous block is a para ......<br />
and (TKMemoParagraph(KMemo1.Blocks[BlockNo-1]).Numbering <> pnuNone) then begin // and previous block is bullet !<br />
SetBullet(TKMemoParagraph(KMemo1.Blocks[ParaBlockNo]), True, TKMemoParagraph(KMemo1.Blocks[BlockNo-1]).Numbering);<br />
exit(False); // lets BS go through to KMemo.<br />
end;<br />
end;<br />
end;<br />
</syntaxhighlight><br />
<br />
If it decides it needs to act, it will what is necessary and return True, indicating to the calling process that it should not allow the Backspace keystroke to be passed on to the KMemo. If the cursor is not in a position that should affect existing bullets, it returns false, the calling procedure will allow the Backspace to go to the KMemo which will use it as expected. The SetBullet() method will increase of decrease the Bullet Level. Further details can be seen in https://github.com/tomboy-notes/tomboy-ng/blob/master/source/editbox.pas<br />
<br />
= Font names, styles, and sizes =<br />
<br />
KMemo supports many of the same font style options as [https://support.smartbear.com/testcomplete/docs/reference/user-forms/helper-objects/tfont/style.html TFont], its Delphi cousin. To set styles:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Block.TextStyle.Font.Name := 'monospace'; // make font fixed width (untested on macOS and Windows)<br />
Block.TextStyle.Font.Name := 'Arial'; // set font to Arial<br />
Block.TextStyle.Font.Style := Block.TextStyle.Font.Style + [fsBold]; // embolden it!<br />
Block.TextStyle.Font.Style := Block.TextStyle.Font.Style + [fsItalic]; // italicize<br />
Block.TextStyle.Font.Style := Block.TextStyle.Font.Style + [fsStrikeout]; // strikeout<br />
Block.TextStyle.Font.Style := Block.TextStyle.Font.Style + [fsUnderline]; // underline<br />
</syntaxhighlight><br />
<br />
You can use similar syntax for font size, color, subscript, and superscript.<br />
<br />
<syntaxhighlight lang="pascal"><br />
Block.TextStyle.Font.Size := 16; // set font size to 16<br />
Block.TextStyle.Font.Color := clRed // use the TColor value of any color<br />
Block.TextStyle.ScriptPosition := tpoSubscript; // subscript<br />
Block.TextStyle.ScriptPosition := tpoSuperscript; // superscript<br />
</syntaxhighlight><br />
<br />
(The snippets above don't exhaustively cover all font style options provided with KMemo)<br />
<br />
= Copy on Selection and Middle Button Paste =<br />
<br />
Unix (ie Linux but not Mac) users have long been used to an alternative copy and paste model. Any text selected is automatically made available to current and other applications with a press of the middle mouse button (or, typically, three finger tap on the touch pad). Most LCL components already do this on Linux but KMemo does not. Its easily implemented by using a capability built into the clipbrd unit.<br />
<br />
When an app detects you have selected some text, it registers the address of a function that knows how to copy that text to where ever it happens to be required. Unlike the 'other' clipboard, the text is not copied until needed. You will use the PrimarySelection TClipboard object already declared in clipbrd unit and a couple of small procedures -<br />
<br />
<syntaxhighlight lang=pascal><br />
uses clipbrd;<br />
....<br />
<br />
procedure TForm1.KMemo1MouseUp(Sender: TObject; Button: TMouseButton;<br />
Shift: TShiftState; X, Y: Integer);<br />
var<br />
Point : TPoint;<br />
LinePos : TKmemoLinePosition;<br />
begin<br />
if Button = mbMiddle then begin<br />
Point := TPoint.Create(X, Y);<br />
PrimaryPaste(KMemo1.PointToIndex(Point, true, true, LinePos));<br />
exit();<br />
end;<br />
if KMemo1.SelAvail and<br />
(Kmemo1.Blocks.SelLength <> 0) then<br />
SetPrimarySelection()<br />
else<br />
UnsetPrimarySelection();<br />
end;<br />
<br />
procedure TForm1.SetPrimarySelection;<br />
var<br />
FormatList: Array [0..1] of TClipboardFormat;<br />
begin<br />
if (PrimarySelection.OnRequest=@PrimaryCopy) then exit;<br />
FormatList[0] := CF_TEXT;<br />
try<br />
PrimarySelection.SetSupportedFormats(1, @FormatList[0]);<br />
PrimarySelection.OnRequest:=@PrimaryCopy;<br />
except<br />
end;<br />
end;<br />
<br />
procedure TForm1.UnsetPrimarySelection;<br />
begin<br />
if PrimarySelection.OnRequest=@PrimaryCopy then<br />
PrimarySelection.OnRequest:=nil;<br />
end; <br />
<br />
procedure TForm1.PrimaryCopy(<br />
const RequestedFormatID: TClipboardFormat; Data: TStream);<br />
var<br />
s : string;<br />
begin<br />
S := KMemo1.Blocks.SelText;<br />
if RequestedFormatID = CF_TEXT then<br />
if length(S) > 0 then<br />
Data.Write(s[1],length(s));<br />
end;<br />
<br />
procedure TForm1.PrimaryPaste(SelIndex : integer);<br />
var<br />
Buff : string;<br />
begin<br />
if PrimarySelection.HasFormat(CF_TEXT) then begin // I don't know if this is useful at all ?<br />
Buff := PrimarySelection().AsText;<br />
if Buff <> '' then<br />
KMemo1.Blocks.InsertPlainText(SelIndex, Buff);<br />
end; <br />
// PrimarySelection is a function that returns a TClipboard object.<br />
// so, here, we call TClipboard.astext, but not the 'normal' clipboard.<br />
end;<br />
<br />
procedure TForm1.FormDestroy(Sender: TObject);<br />
begin<br />
UnsetPrimarySelection;<br />
end;</syntaxhighlight> <br />
<br />
Note we copy the address of PrimaryCopy() into the PrimarySelection system. As other applications may well be calling this function, you must ensure the function parameter list remains as presented. Primary paste on the other hand is up to you. It needs to know how to paste an arbitrary bit of text into your application.<br />
<br />
The Lazarus IDE does more than just plain text, see synedit.pp and search for PrimarySelection - thanks to Martin_fr for the pointer.<br />
<br />
Further research is indicated -<br />
* Should we {$ifdef LINUX} it so it does no harm in Mac and Windows. Lazarus allows windows users to do this within the app. Mac users have no concept of a middle mouse button so can never use it. Test.<br />
* Need to do more through tests with UTF8.<br />
* Is the CF_TEXT test in PrimaryPaste useful ? Is there a better way to test if content is available ?<br />
<br />
= Lines =<br />
<br />
You can refer to individual lines of text within the KMemo using KMemo1.Blocks.Lines.Items[I] which returns a TKMemoLine. It has useful data in it about the line including start and end blocks and indexes. Note in this case, 'Line' refers to a line as currently displayed in KMemo. Just to be clear, that means, for example, altering the width of the KMemo moves the text wrapping point and alters the line numbering.<br />
<br />
You can get the (0 based) line number the cursor is currently on or the text that line contains -<br />
<syntaxhighlight lang="pascal"><br />
LineNo := Kmemo1.Blocks.IndexToLineIndex(KMemo1.Blocks.RealSelStart);<br />
debugln('Current Line number ' + inttostr(LineNo));<br />
debugln('[' + Kmemo1.Blocks.LineText[LineNo] + ']');<br />
</syntaxhighlight><br />
<br />
=Locking=<br />
<br />
As your app grows, you may find that some processes start to take a significant amount of time. Here, Locking is not refering to where we lock a process to avoid interruptions, it actually speeds up the process, and quite substantially. If you have a series of (write) operations to perform on the contents of a KMemo, apply the lock before you start, release it when finished. This sort of thing -<br />
<br />
<syntaxhighlight lang="pascal">try<br />
KMemo1.Blocks.LockUpdate;<br />
while BlockNo < Kmemo1.Blocks.Count do begin<br />
SomeCrazyProcedure(KMemo.Blocks.Items[BlockNo]);<br />
inc(BlockNo);<br />
end<br />
finally<br />
KMemo1.Blocks.UnlockUpdate;<br />
end;</syntaxhighlight><br />
<br />
Locking does not seem to make much difference where you are not writing to the contents nor for one-off procedures. It makes a big difference when you are doing a lot of stuff all at once.<br />
<br />
* WARNING: Do not use ''KMemo1.LockUpdate'' (without ''.Blocks.'')!<br />
* WARNING: If your application starts behaving oddly (selecting text happens slowly or does not happen at all) you have most likely missed to do ''KMemo1.Blocks.UnlockUpdate;'' Makes sense to use a try..finally to ensure unlock happens.<br />
* WARNING: Do not attempt to move the cursor while locked. Bad things happen ....<br />
<br />
* '''WARNING''': LockUpdate uses an internal counter (type int32), to prevent undesired unlocking by nested routines. So in order to unlock something you need to call .UnlockUpdate as many times as you have called .LockUpdate. <br />
Example:<br />
<syntaxhighlight lang="pascal"><br />
KMemo1.Blocks.LockUpdate;<br />
KMemo1.Blocks.LockUpdate;<br />
KMemo1.Blocks.LockUpdate;<br />
<br />
KMemo1.Blocks.UnLockUpdate; // Kmemo1 is still locked<br />
KMemo1.Blocks.UnlockUpdate; // Kmemo1 is still locked<br />
KMemo1.Blocks.UnLockUpdate; // Kmemo1 is unlocked<br />
</syntaxhighlight><br />
<br />
In order to make sure that something is unlocked you could:<br />
<br />
<syntaxhighlight lang="pascal"><br />
for i:=0 to 2147483647 do // or some counter with lower value to prevent running the loop forever<br />
if KMemo1.Blocks.UpdateUnlocked=False <br />
then KMemo1.Blocks.UnLockUpdate<br />
else break;<br />
if KMemo1.Blocks.UnLockUpdate=False then {error handling or whatever}<br />
</syntaxhighlight><br />
<br />
=Bugs=<br />
<br />
Yep, it has some bugs and some not yet implemented features. But not many ! Please report bugs at the link below. <br />
<br />
'''Canceling Bullets'''<br />
<br />
Late 2017 its been noted that there is a problem canceling Bullets on linux. Its fixed in the 2017-09-24 github version.<br />
<br />
'''Direction of Selection'''<br />
<br />
You select text (with a mouse) either from left to right or from right to left. You may notice that in KMemo, this direction is important if you use KMemo1.SelStart etc. This is '''NOT''' a bug, in fact, if it matters, you should be using KMemo1.RealSelStart (etc) instead.<br />
<br />
==Copy and Paste Bug==<br />
<br />
Kmemo, as of April, 2018 and previous has a Unicode bug that affects Linux and Mac. It shows up if you copy unicode characters between KMemos but not, for example if you copy and paste the same text via an intermediate (non KMemo) app. The reason is that when copying directly between KMemos the text is copied in RTF format (to retain any formatting). A similar and directly related issue may occur when reading or writing RTF that contains UNicode text or Bullet Points, noting the Bullet Point is, itself, a Unicode character.<br />
<br />
In fact this bug actually consists of two unrelated bugs, one affecting the copying (or writeRTF) behaviour, the other affecting pasting (or loadRTF).<br />
<br />
==The Copy (or WriteRTF) Problem==<br />
<br />
On windows, the call to StringToAnsiString(C, FCodePage); at kmemortf#4098 converts a single Unicode multibyte character (in C) to single byte (code page aware ?). For example the english pound becomes #163. On linux, it is not changed, that is, it remains a two byte unicode character. That is because in LConvEncoding#7392, the function ConvertEncoding(const s, FromEncoding, ToEncoding: string<br />
): string; has decided the string does not need to be converted. Effectivly, as GetDefautTextEncoding() returns 'utf8'as we are asking it to convert UTF8 to UTF8. So, quite resonably, It decides to pass the string back as it is, still in multibyte form.<br />
<br />
However in kmemortf#4067 there is a procedure TKMemoRTFWriter.WriteUnicodeString(const AText: TKString) that gets the string 'converted' above.<br />
At line#4099 there is - <br />
<br />
<syntaxhighlight lang=pascal><br />
if (Ansi <> '') and (Ansi <> #0) then<br />
begin<br />
S := AnsiString(Format('%s\''%.2x', [S, Ord(Ansi[1])]));<br />
WasAnsi := True;<br />
end;<br />
</syntaxhighlight><br />
<br />
This code assumes all char in S are single byte but as discussed, on a Linux/Mac, being UTF8 ANSI codepage system, that is not the case.<br />
<br />
Interestingly, in the next few lines of WriteUnicodeString() is code obviously intended to deal with multibyte characters. But it does not get used because line#4099 has already stepped in.<br />
<br />
So, replace the test line #4099, the block becomes - <br />
<br />
<syntaxhighlight lang=pascal><br />
if (length(Ansi) = 1) and (Ansi <> #0) then<br />
begin<br />
S := AnsiString(Format('%s\''%.2x', [S, Ord(Ansi[1])]));<br />
WasAnsi := True;<br />
end;<br />
</syntaxhighlight><br />
<br />
This will ensure that a multibyte char is passed on to next part of the procedure as was probably intended.<br />
<br />
==Paste (or LoadRTF) Issue==<br />
<br />
KMemortf ignores "listtext" as an RTF tag (kmemortf#2381). It sets FActiveState.Group to RGUNKNOWN so other methods know not to do anything with it. However, it is still processed to some extent. In particular, if the listtext params include a unicode character, ReadSpecialCharacter() will set FIgnoreChars to one so we don't use the "code page escape" character In AddText(). But we are not using any part of the listtext section so we don't call AddText() on the "code page escape" character and therefore we don't clear FIgnoreChars until we move on to the next RTF section. <br />
<br />
For example KMemo with a Bullet Point writes an RTF file that looks like this -<br />
<br />
{\listtext{\f0\fs20\cf1 \u8226\'3F\tab }} <br />
<br />
The "u8226" is a bullet point character (it is ignored by KMemo). The "'3F' is the "?" character, there in case the app cannot handle Unicode escapes.<br />
<br />
After that listtext block is processed FIgnore insists we still need to drop the first displayable character. So we loose first character of text following a bullet point.<br />
<br />
The Solution - Line 2712 at the end of procedure TKMemoRTFReader.ReadSpecialCharacter(...) -<br />
if SetIgnoreChars then <br />
FIgnoreChars := FIgnoreCharsAfterUnicode; <br />
<br />
becomes - <br />
<br />
if SetIgnoreChars and (FActiveState.Group <> RGUNKNOWN) then // DRB - April 2018<br />
FIgnoreChars := FIgnoreCharsAfterUnicode;<br />
<br />
=Installation =<br />
<br />
KMemo must be installed as part of the KControls Package. As is usual, its easy. You should be using a recent version of FPC and Lazarus. Download the Github vesion of KControls, unzip and put it somewhere suitable. Fire up Lazarus, click Packages, click "Open Pack File (.lpk)" and find the kcontrolslaz.lpk file in the kit you downloaded.<br />
<br />
Click Compile. <br />
<br />
Lazarus will do its install thing, shortly will ask you about rebuilding, answer yes, it will then shutdown. On startup, you will find an extra row of components at the far right, KMemo amongst them.<br />
<br />
If you find this as valuable a component as I did, I suggest you hit the link below and thank TK, it is one hell of an effort !<br />
<br />
= See Also =<br />
<br />
* [[KControls]] - the package KMemo is part of.<br />
* https://github.com/kryslt/KControls - Github repository.<br />
* [[RichMemo]] - an alternative package.<br />
* [[UTF8_strings_and_characters]]<br />
* How to hide / show the caret? - TBD<br />
<br />
[[Category:Components]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=KControls/KMemo_notes&diff=158163KControls/KMemo notes2024-01-29T06:30:11Z<p>Dbannon: revised hack to make Bullets work as an end user expects</p>
<hr />
<div>= Introduction =<br />
<br />
This page is about the KMemo Component, a part of [[KControls]]. KMemo provides a Cross Platform (Linux, Windows, macOS) memo capable of a range of text font styles, colours and similar. Its also capable of showing images and a range of other things but that may need to be dealt with at a later stage. And, importantly, it really does work pretty much the same way across Linux, Windows and Mac. Possibly other platforms as well but I cannot speak authoritatively there.<br />
<br />
The content presented here is in addition to the manual distributed with the KControls package. Its written by a KMemo user, not the author and could contain errors, omissions and possibly outright lies ! Someone else will undoubtedly use a different set of methods so that person is urged to document what they find too. And correct any errors they find here.<br />
<br />
= Underlying structure =<br />
<br />
Its important, when using KMemo, to understand how the memo content and its meta data is stored. Everything is in a Block, and KMemo itself has a Blocks property. A Block has a Text property that contains the text being displayed, a Font property and a number of others controlling how the text is displayed. Every change to font style, colour, size and so on requires a new block. Block classes discussed here include TKMemoTextBlock, TKMemoParagraph, TKMemoHyperlink. A TKMemoParagraph appears between two paragraphs.<br />
<br />
This '''line of ''text is an''' example.'' <br />
<br />
It will contain 5 blocks. <br />
* Block 0 "This " - normal font.<br />
* Block 1 "line of " - bold<br />
* Block 2 "text is an " - bold and italic<br />
* Block 3 "example." - italic<br />
* Block 4 - a paragraph marker.<br />
<br />
The KMemo, Block and Blocks classes have a large number of other Properties and Methods, far too many to possibly document here. And there are also many associated classes. Lazarus will prompt you with them and the names are reasonably intuitive.<br />
<br />
=Inserting Text=<br />
<br />
Easy. KMemo1.Blocks.AddTextBlock(AString). That will append the block at the end of any existing blocks. You can add a number after the string parameter and it will be inserted at that BlockIndex. Its a function, returning the TKMemoBlock so, once created you can alter how that text is presented.<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure Form1.AddText();<br />
var<br />
TB: TKMemoTextBlock;<br />
begin<br />
TB := KMemo1.Blocks.AddTextBlock(InStr);<br />
TB.TextStyle.Font.Size := 16;<br />
TB.TextStyle.Font.Style := TB.TextStyle.Font.Style + [fsBold];<br />
end;</syntaxhighlight><br />
<br />
Now, presumably, AddTextBlock() has allocated some memory for that TextBlock, we assume it will Free it when appropriate.<br />
<br />
If you wish to insert some text at a particular spot that will take on the font characteristics of the insertion point, its even easier. Several functions will return a SelIndex (its just a count of characters starting at zero) -<br />
<br />
<syntaxhighlight lang="pascal">KMemo1.Blocks.InsertPlainText(SelIndex, 'some bit of text to insert');</syntaxhighlight><br />
<br />
=Selections=<br />
<br />
... On our selection ... (sorry, Aussie joke)<br />
<br />
Kmemo offers a couple of functions, KMemo1.Blocks.RealSelStart, KMemo1.Blocks.RealSelEnd that return (read only) SelectionIndexes. ~End is always greater (or equal to) than ~Start unlike KMemo1.SelStart, Kmemo1.SelEnd which are sensitive to the direction the user wiped the mouse. KMemo1.Blocks.RealSelStart tracks the cursor position while a user is typing.<br />
<br />
The function KMemo1..Blocks.SelText returns the selected text (if any) irrespective of intervening block structure. Note that it might include newline characters.<br />
<br />
=Miscellaneous=<br />
<br />
NOTE: By default upon creation of a table, a text block containing a new line is created within it. Usually it has to be removed by:<br />
<br />
<syntaxhighlight lang="pascal">procedure Form1.AddText();<br />
KMemo1.Blocks.Clear;end;</syntaxhighlight><br />
<br />
'''WARNING:''' TKmemo does '''not''' support RightToLeft languages.<br />
<br />
=Playing With Blocks=<br />
<br />
You will get used to working with the two indexes, one (utf8) character based and the other Block based. Both start at zero.<br />
<br />
The Blocks class has a property, Items that can let you address an individual block, so KMemo1.Blocks.Items[BlockNo] is a particular block. And it has a whole bunch of properties and methods.<br />
<br />
There are KMemo1.Blocks.Count blocks in a KMemo. And there is length(KMemo1.Blocks.Text) characters but only in a Unix based system such as Linux or macOS. All systems allow one character for a Paragraph marker in the KMemo itself but Windows, being Windows puts a CR/LF, (#13#10) at the end of each paragraph in KMemo1.Blocks.Text. So you need allow for that, for an example, see below about Searching.<br />
<br />
'''How to convert between the two ? Like this'''<br />
<br />
<syntaxhighlight lang="pascal">var <br />
BlockNo, CharNo, LocalIndex : longint;<br />
begin<br />
CharNo := SomeValue;<br />
BlockNo := Kmemo1.Blocks.IndexToBlockIndex(CharNo, LocalIndex);<br />
</syntaxhighlight> <br />
<br />
BlockNo now has what block number CharNo points to and LocalIndex tells us how far in the block we are. Useful information, if LocalIndex is 0, we are at the start of a block, if its length(KMemo1.Blocks.Items[BlockNo].Text)-1 we must be at the end of a block. Dont forget that the Text component of a Block is, effectively, a Pascal string and it starts at 1, not zero. So, the first character on the first line of a KMemo is<br />
<br />
* KMemo.Blocks.Items[0].Text[1] <br />
* TKMemoSelectionIndex = 0<br />
* and Kmemo1.Blocks.IndexToBlockIndex(0, LocalIndex); // will return (block) 0 and set LocalIndex to 0<br />
<br />
'''The reverse''' -<br />
<br />
<syntaxhighlight lang="pascal">CharNo := KMemo1.Blocks.BlockToIndex(KMemo1.Blocks.Items[BlockNo]); </syntaxhighlight><br />
<br />
will convert a blocknumber to SelectionIndex type of number. A SelectionIndex is a 0 based count of UTF8 characters, not bytes.<br />
<br />
When iterating over blocks, important to know what each block is -<br />
<br />
<syntaxhighlight lang="pascal">if KMemo1.Blocks.Items[BlockNo].ClassNameIs('TKMemoHyperlink') then<br />
URL := TKMemoHyperlink(KMemo1.Blocks.Items[BlockNo].URL);</syntaxhighlight><br />
<br />
Here we have found out that the Block at BlockNo is a Hyperlink block, we can therefore cast it to TKMemoHyperlink and get the URL stored there previously.<br />
<br />
<syntaxhighlight lang="pascal">Kmemo1.Blocks.Delete(BlockNo);</syntaxhighlight> <br />
<br />
To delete a block. The source says parameter is an integer, not a TKMemoBlockIndex so you may miss it when looking for something to delete a block. But that appears to be the one to use ! <br />
<br />
<syntaxhighlight lang="pascal">KMemo1.Blocks.DeleteChar(Index);</syntaxhighlight> <br />
<br />
Can also be a bit confusing. You would expect it to delete the character pointed to by Index, and yes, it might. But if there is another area of Text selected when you call it, instead, it will delete that other area of text. And that can be quite a surprise ! So, something like:<br />
<br />
<syntaxhighlight lang="pascal"><br />
KMemo1.Select(Index, 0);<br />
while Cnt < Len do begin <br />
KMemo1.Blocks.DeleteChar(Index); <br />
inc(Cnt);<br />
end;<br />
</syntaxhighlight><br />
<br />
And, you may need to restore the selection points afterwards.<br />
<br />
As a paragraph may consist of many blocks and one para block, its useful to know where that para block is. KMemo1.NearestParagraphIndex returns the index of the FOLLOWING para block (obviously, not necessarily the 'nearest').<br />
<br />
=Hyperlinks=<br />
<br />
TKMemo supports text-only hyperlinks. As of October 2017 Hyperlinks containing images are not supported.<br />
<br />
OK, lets suppose you have a KMemo full of text and you want to make a word of it a hyperlink. It will be blue, have an underline and when clicked, do something. The hyperlink will be one block (remember, everything is a block), so if the text we want to convert is already a discrete block, easy, delete it and then insert a new hyperlink block with the same text. However, its more likely we need to split a block. The process is to delete the characters that we want to become the link, split the block at that spot, create a TKMemoHyperlink block, configure it and insert it between the split blocks. <br />
<br />
This procedure will make a hyperlink at the character position, Index and it will be Len long. It exits doing nothing if that spot is already a hyperlink.<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.MakeDemoLink(Index, Len : longint);<br />
var<br />
Hyperlink: TKMemoHyperlink;<br />
BlockNo, Offset : longint;<br />
DontSplit : Boolean = false;<br />
Link : ANSIString;<br />
begin<br />
// Is it already a Hyperlink ?<br />
BlockNo := KMemo1.Blocks.IndexToBlockIndex(Index, Offset);<br />
if KMemo1.Blocks.Items[BlockNo].ClassNameIs('TKHyperlink') then exit();<br />
Link := copy(KMemo1.Blocks.Items[BlockNo].Text, Offset+1, Len);<br />
if length(Kmemo1.Blocks.Items[BlockNo].Text) = Len then DontSplit := True;<br />
TheKMemo.SelStart := Index; <br />
TheKMemo.SelLength := Len;<br />
TheKMemo.ClearSelection();<br />
if not DontSplit then BlockNo := KMemo1.SplitAt(Index);<br />
Hyperlink := TKMemoHyperlink.Create;<br />
Hyperlink.Text := Link;<br />
Hyperlink.OnClick := @OnUserClickLink;<br />
KMemo1.Blocks.AddHyperlink(Hyperlink, BlockNo);<br />
end;<br />
</syntaxhighlight> <br />
<br />
Now, danger Will Robertson ! This function will fail if you pass it parameters of text that are not within one block. But only because of the "Link := copy(..." line. Easy to fix with a few extra lines of code.<br />
<br />
And you may need to first record the existing selection and restore it afterwards too.<br />
<br />
Oh, and whats this "@OnUserClickLink" ? Its the address of a procedure "OnUserClickLink()" that might be defined like this -<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TEditBoxForm.OnUserClickLink(sender : TObject);<br />
begin<br />
showmessage('Wow, someone clicked ' + TKMemoHyperlink(Sender).Text);<br />
end;<br />
</syntaxhighlight><br />
<br />
=Search=<br />
<br />
Despite all those methods, I could not find a Search function. But if your application is just text based (ie no images or nested containers) its not that difficult to search KMemo1.Blocks.Text, treat it as an ANSIString. On Unix like systems, the position in KMemo1.Blocks.Text can be used directly as a TKMemoSelectionIndex. Sadly, in Windows, KMemo1.Blocks.Text has Window's two character line endings, but in KMemo itself, only one char is reserved for line endings. So, count the #13 characters from the start to the position of interest and subtract that from what becomes our TKMemoSelection index. <br />
<br />
This UTF8 compliant function will return 0 or the TKMemoSelectionIndex of the searched for term. It depends on the LazUTF8 unit so add it to your uses clause. It can be called repeatably, passing the previously found index plus 1 each time to find multiple incidences of a term. Set MoveCursor to True and it will show the user where the searched for term is. You will note most of the code is about fixing up the Windows problem.<br />
<br />
<syntaxhighlight lang="pascal"><br />
function TForm1.Search(Term : ANSIString; StartAt : longint = 1; MoveCursor : Boolean = False) : longint;<br />
var<br />
Ptr, EndP : pchar;<br />
Offset : longint;<br />
NumbCR : longint;<br />
begin<br />
Result := UTF8Pos(Term, KMemo1.Blocks.text, StartAt);<br />
{$IFDEF WINDOWS} // Sadley we need to subtract the extra CR windows adds to a newline<br />
if Result = 0 then exit();<br />
NumbCR := 0;<br />
Ptr := PChar(KMemo1.Blocks.text);<br />
EndP := Ptr + Result-1;<br />
while Ptr < EndP do begin<br />
if Ptr^ = #13 then inc(NumbCR);<br />
inc(Ptr);<br />
end;<br />
Result := Result - NumbCR;<br />
{$ENDIF} // does no harm in Unix but adds 66mS with my test note.<br />
if MoveCursor then begin<br />
KMemo1.SelStart := Result;<br />
KMemo1.SelEnd := Result;<br />
KMemo1.SetFocus;<br />
end;<br />
end; <br />
</syntaxhighlight><br />
<br />
=Bullets=<br />
<br />
Bullets are a popular way to display a series of (maybe) short items. KMemo does have an "out of the box" bullet capability but not every part of it works exactly as people generally expect. The underlying issue is that KMemo records the "bulletness" of a bit of text in the paragraph marker at the end of the text. This results in some unexpected (to the end user) behaviour, particularly when using backspace key at beginning or end of a line of bullet text -<br />
* If the user backspaces from the end, perhaps hoping to delete the last character there, instead, they will delete the paragraph marker (not surprising) and remove the bullet attribute from that line of text (thats is surprising to end user).<br />
* If the user places the cursor at start of a bullet text and presses backspace, expecting to cancel the "bulletness", instead, it acts as a backspace should and removes the leading paragraph marked and merges the line with the line above. And if the line above was not already a bullet, it now becomes one !<br />
<br />
There are a large number of subcase of the above. Each has to be dealt with. The solution at the moment is to intercept the backspace key and, if its at either end of a bullet line of text, prevent it being passed on and doing what is expected there and then. This works but but is complicated.<br />
<br />
Firstly, in KMemo1KeyDown() we look at every keystroke, we have to any way to intercept any one of the ctrl or similar key strokes. But we are very interested in a Backspace -<br />
<syntaxhighlight lang="pascal"><br />
procedure TEditBoxForm.KMemo1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);<br />
begin<br />
// do all sorts of stuff and exit early if not a Backspace<br />
if BackSpaceBullet() then <br />
Key := 0; // eat that key because it was used in BackSpaceBullet()<br />
<br />
end;<br />
</syntaxhighlight><br />
<br />
OK, now BackspaceBullet() will check to see if the cursor is in a position near a bullet that needs our attention.<br />
<syntaxhighlight lang="pascal"><br />
function TEditBoxForm.BackSpaceBullet() : boolean;<br />
var<br />
BlockNo, PosInBlock : longint;<br />
ParaBlockNo : integer = -1;<br />
IsBulletPara : boolean = false; // The para the cursor is in.<br />
begin<br />
Result := false;<br />
ParaBlockNo := Kmemo1.NearestParagraphIndex; // Thats para blockno. that controls where cursor is.<br />
if ParaBlockNo < 2 then exit; // Dont mess with title.<br />
BlockNo := kmemo1.Blocks.IndexToBlockIndex(KMemo1.RealSelStart, PosInBlock); // Block with cursor<br />
if PosInBlock > 0 then exit; // not the sort of BS we are looking for.<br />
// ======== BS when on first char of bullet line.========<br />
if TKMemoParagraph(KMemo1.Blocks[ParaBlockNo]).Numbering <> pnuNone then begin // ie, this is a bullet para.<br />
IsBulletPara := True;<br />
if kmemo1.Blocks.Items[BlockNo-1].ClassNameIs('TkMemoParagraph') then begin // Cursor is on first char of a bullet para.<br />
SetBullet(TKMemoParagraph(KMemo1.Blocks[ParaBlockNo]), False);<br />
exit(True); // My work here is done.<br />
end;<br />
end;<br />
// If previous para is already a bullet and current one is not, we set current one to same<br />
// bullet level as previous and allow BS to do the rest. BlockNo would have been set above<br />
// and we know we are on the first char of the paragraph.<br />
if (BlockNo > 0) and (not IsBulletPara) then begin // Cursor is on first char of a non-bullet para.<br />
if kmemo1.Blocks.Items[BlockNo-1].ClassNameIs('TkMemoParagraph') // previous block is a para ......<br />
and (TKMemoParagraph(KMemo1.Blocks[BlockNo-1]).Numbering <> pnuNone) then begin // and previous block is bullet !<br />
SetBullet(TKMemoParagraph(KMemo1.Blocks[ParaBlockNo]), True, TKMemoParagraph(KMemo1.Blocks[BlockNo-1]).Numbering);<br />
exit(False); // lets BS go through to KMemo.<br />
end;<br />
end;<br />
end;<br />
</syntaxhighlight><br />
<br />
= Font names, styles, and sizes =<br />
<br />
KMemo supports many of the same font style options as [https://support.smartbear.com/testcomplete/docs/reference/user-forms/helper-objects/tfont/style.html TFont], its Delphi cousin. To set styles:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Block.TextStyle.Font.Name := 'monospace'; // make font fixed width (untested on macOS and Windows)<br />
Block.TextStyle.Font.Name := 'Arial'; // set font to Arial<br />
Block.TextStyle.Font.Style := Block.TextStyle.Font.Style + [fsBold]; // embolden it!<br />
Block.TextStyle.Font.Style := Block.TextStyle.Font.Style + [fsItalic]; // italicize<br />
Block.TextStyle.Font.Style := Block.TextStyle.Font.Style + [fsStrikeout]; // strikeout<br />
Block.TextStyle.Font.Style := Block.TextStyle.Font.Style + [fsUnderline]; // underline<br />
</syntaxhighlight><br />
<br />
You can use similar syntax for font size, color, subscript, and superscript.<br />
<br />
<syntaxhighlight lang="pascal"><br />
Block.TextStyle.Font.Size := 16; // set font size to 16<br />
Block.TextStyle.Font.Color := clRed // use the TColor value of any color<br />
Block.TextStyle.ScriptPosition := tpoSubscript; // subscript<br />
Block.TextStyle.ScriptPosition := tpoSuperscript; // superscript<br />
</syntaxhighlight><br />
<br />
(The snippets above don't exhaustively cover all font style options provided with KMemo)<br />
<br />
= Copy on Selection and Middle Button Paste =<br />
<br />
Unix (ie Linux but not Mac) users have long been used to an alternative copy and paste model. Any text selected is automatically made available to current and other applications with a press of the middle mouse button (or, typically, three finger tap on the touch pad). Most LCL components already do this on Linux but KMemo does not. Its easily implemented by using a capability built into the clipbrd unit.<br />
<br />
When an app detects you have selected some text, it registers the address of a function that knows how to copy that text to where ever it happens to be required. Unlike the 'other' clipboard, the text is not copied until needed. You will use the PrimarySelection TClipboard object already declared in clipbrd unit and a couple of small procedures -<br />
<br />
<syntaxhighlight lang=pascal><br />
uses clipbrd;<br />
....<br />
<br />
procedure TForm1.KMemo1MouseUp(Sender: TObject; Button: TMouseButton;<br />
Shift: TShiftState; X, Y: Integer);<br />
var<br />
Point : TPoint;<br />
LinePos : TKmemoLinePosition;<br />
begin<br />
if Button = mbMiddle then begin<br />
Point := TPoint.Create(X, Y);<br />
PrimaryPaste(KMemo1.PointToIndex(Point, true, true, LinePos));<br />
exit();<br />
end;<br />
if KMemo1.SelAvail and<br />
(Kmemo1.Blocks.SelLength <> 0) then<br />
SetPrimarySelection()<br />
else<br />
UnsetPrimarySelection();<br />
end;<br />
<br />
procedure TForm1.SetPrimarySelection;<br />
var<br />
FormatList: Array [0..1] of TClipboardFormat;<br />
begin<br />
if (PrimarySelection.OnRequest=@PrimaryCopy) then exit;<br />
FormatList[0] := CF_TEXT;<br />
try<br />
PrimarySelection.SetSupportedFormats(1, @FormatList[0]);<br />
PrimarySelection.OnRequest:=@PrimaryCopy;<br />
except<br />
end;<br />
end;<br />
<br />
procedure TForm1.UnsetPrimarySelection;<br />
begin<br />
if PrimarySelection.OnRequest=@PrimaryCopy then<br />
PrimarySelection.OnRequest:=nil;<br />
end; <br />
<br />
procedure TForm1.PrimaryCopy(<br />
const RequestedFormatID: TClipboardFormat; Data: TStream);<br />
var<br />
s : string;<br />
begin<br />
S := KMemo1.Blocks.SelText;<br />
if RequestedFormatID = CF_TEXT then<br />
if length(S) > 0 then<br />
Data.Write(s[1],length(s));<br />
end;<br />
<br />
procedure TForm1.PrimaryPaste(SelIndex : integer);<br />
var<br />
Buff : string;<br />
begin<br />
if PrimarySelection.HasFormat(CF_TEXT) then begin // I don't know if this is useful at all ?<br />
Buff := PrimarySelection().AsText;<br />
if Buff <> '' then<br />
KMemo1.Blocks.InsertPlainText(SelIndex, Buff);<br />
end; <br />
// PrimarySelection is a function that returns a TClipboard object.<br />
// so, here, we call TClipboard.astext, but not the 'normal' clipboard.<br />
end;<br />
<br />
procedure TForm1.FormDestroy(Sender: TObject);<br />
begin<br />
UnsetPrimarySelection;<br />
end;</syntaxhighlight> <br />
<br />
Note we copy the address of PrimaryCopy() into the PrimarySelection system. As other applications may well be calling this function, you must ensure the function parameter list remains as presented. Primary paste on the other hand is up to you. It needs to know how to paste an arbitrary bit of text into your application.<br />
<br />
The Lazarus IDE does more than just plain text, see synedit.pp and search for PrimarySelection - thanks to Martin_fr for the pointer.<br />
<br />
Further research is indicated -<br />
* Should we {$ifdef LINUX} it so it does no harm in Mac and Windows. Lazarus allows windows users to do this within the app. Mac users have no concept of a middle mouse button so can never use it. Test.<br />
* Need to do more through tests with UTF8.<br />
* Is the CF_TEXT test in PrimaryPaste useful ? Is there a better way to test if content is available ?<br />
<br />
= Lines =<br />
<br />
You can refer to individual lines of text within the KMemo using KMemo1.Blocks.Lines.Items[I] which returns a TKMemoLine. It has useful data in it about the line including start and end blocks and indexes. Note in this case, 'Line' refers to a line as currently displayed in KMemo. Just to be clear, that means, for example, altering the width of the KMemo moves the text wrapping point and alters the line numbering.<br />
<br />
You can get the (0 based) line number the cursor is currently on or the text that line contains -<br />
<syntaxhighlight lang="pascal"><br />
LineNo := Kmemo1.Blocks.IndexToLineIndex(KMemo1.Blocks.RealSelStart);<br />
debugln('Current Line number ' + inttostr(LineNo));<br />
debugln('[' + Kmemo1.Blocks.LineText[LineNo] + ']');<br />
</syntaxhighlight><br />
<br />
=Locking=<br />
<br />
As your app grows, you may find that some processes start to take a significant amount of time. Here, Locking is not refering to where we lock a process to avoid interruptions, it actually speeds up the process, and quite substantially. If you have a series of (write) operations to perform on the contents of a KMemo, apply the lock before you start, release it when finished. This sort of thing -<br />
<br />
<syntaxhighlight lang="pascal">try<br />
KMemo1.Blocks.LockUpdate;<br />
while BlockNo < Kmemo1.Blocks.Count do begin<br />
SomeCrazyProcedure(KMemo.Blocks.Items[BlockNo]);<br />
inc(BlockNo);<br />
end<br />
finally<br />
KMemo1.Blocks.UnlockUpdate;<br />
end;</syntaxhighlight><br />
<br />
Locking does not seem to make much difference where you are not writing to the contents nor for one-off procedures. It makes a big difference when you are doing a lot of stuff all at once.<br />
<br />
* WARNING: Do not use ''KMemo1.LockUpdate'' (without ''.Blocks.'')!<br />
* WARNING: If your application starts behaving oddly (selecting text happens slowly or does not happen at all) you have most likely missed to do ''KMemo1.Blocks.UnlockUpdate;'' Makes sense to use a try..finally to ensure unlock happens.<br />
* WARNING: Do not attempt to move the cursor while locked. Bad things happen ....<br />
<br />
* '''WARNING''': LockUpdate uses an internal counter (type int32), to prevent undesired unlocking by nested routines. So in order to unlock something you need to call .UnlockUpdate as many times as you have called .LockUpdate. <br />
Example:<br />
<syntaxhighlight lang="pascal"><br />
KMemo1.Blocks.LockUpdate;<br />
KMemo1.Blocks.LockUpdate;<br />
KMemo1.Blocks.LockUpdate;<br />
<br />
KMemo1.Blocks.UnLockUpdate; // Kmemo1 is still locked<br />
KMemo1.Blocks.UnlockUpdate; // Kmemo1 is still locked<br />
KMemo1.Blocks.UnLockUpdate; // Kmemo1 is unlocked<br />
</syntaxhighlight><br />
<br />
In order to make sure that something is unlocked you could:<br />
<br />
<syntaxhighlight lang="pascal"><br />
for i:=0 to 2147483647 do // or some counter with lower value to prevent running the loop forever<br />
if KMemo1.Blocks.UpdateUnlocked=False <br />
then KMemo1.Blocks.UnLockUpdate<br />
else break;<br />
if KMemo1.Blocks.UnLockUpdate=False then {error handling or whatever}<br />
</syntaxhighlight><br />
<br />
=Bugs=<br />
<br />
Yep, it has some bugs and some not yet implemented features. But not many ! Please report bugs at the link below. <br />
<br />
'''Canceling Bullets'''<br />
<br />
Late 2017 its been noted that there is a problem canceling Bullets on linux. Its fixed in the 2017-09-24 github version.<br />
<br />
'''Direction of Selection'''<br />
<br />
You select text (with a mouse) either from left to right or from right to left. You may notice that in KMemo, this direction is important if you use KMemo1.SelStart etc. This is '''NOT''' a bug, in fact, if it matters, you should be using KMemo1.RealSelStart (etc) instead.<br />
<br />
==Copy and Paste Bug==<br />
<br />
Kmemo, as of April, 2018 and previous has a Unicode bug that affects Linux and Mac. It shows up if you copy unicode characters between KMemos but not, for example if you copy and paste the same text via an intermediate (non KMemo) app. The reason is that when copying directly between KMemos the text is copied in RTF format (to retain any formatting). A similar and directly related issue may occur when reading or writing RTF that contains UNicode text or Bullet Points, noting the Bullet Point is, itself, a Unicode character.<br />
<br />
In fact this bug actually consists of two unrelated bugs, one affecting the copying (or writeRTF) behaviour, the other affecting pasting (or loadRTF).<br />
<br />
==The Copy (or WriteRTF) Problem==<br />
<br />
On windows, the call to StringToAnsiString(C, FCodePage); at kmemortf#4098 converts a single Unicode multibyte character (in C) to single byte (code page aware ?). For example the english pound becomes #163. On linux, it is not changed, that is, it remains a two byte unicode character. That is because in LConvEncoding#7392, the function ConvertEncoding(const s, FromEncoding, ToEncoding: string<br />
): string; has decided the string does not need to be converted. Effectivly, as GetDefautTextEncoding() returns 'utf8'as we are asking it to convert UTF8 to UTF8. So, quite resonably, It decides to pass the string back as it is, still in multibyte form.<br />
<br />
However in kmemortf#4067 there is a procedure TKMemoRTFWriter.WriteUnicodeString(const AText: TKString) that gets the string 'converted' above.<br />
At line#4099 there is - <br />
<br />
<syntaxhighlight lang=pascal><br />
if (Ansi <> '') and (Ansi <> #0) then<br />
begin<br />
S := AnsiString(Format('%s\''%.2x', [S, Ord(Ansi[1])]));<br />
WasAnsi := True;<br />
end;<br />
</syntaxhighlight><br />
<br />
This code assumes all char in S are single byte but as discussed, on a Linux/Mac, being UTF8 ANSI codepage system, that is not the case.<br />
<br />
Interestingly, in the next few lines of WriteUnicodeString() is code obviously intended to deal with multibyte characters. But it does not get used because line#4099 has already stepped in.<br />
<br />
So, replace the test line #4099, the block becomes - <br />
<br />
<syntaxhighlight lang=pascal><br />
if (length(Ansi) = 1) and (Ansi <> #0) then<br />
begin<br />
S := AnsiString(Format('%s\''%.2x', [S, Ord(Ansi[1])]));<br />
WasAnsi := True;<br />
end;<br />
</syntaxhighlight><br />
<br />
This will ensure that a multibyte char is passed on to next part of the procedure as was probably intended.<br />
<br />
==Paste (or LoadRTF) Issue==<br />
<br />
KMemortf ignores "listtext" as an RTF tag (kmemortf#2381). It sets FActiveState.Group to RGUNKNOWN so other methods know not to do anything with it. However, it is still processed to some extent. In particular, if the listtext params include a unicode character, ReadSpecialCharacter() will set FIgnoreChars to one so we don't use the "code page escape" character In AddText(). But we are not using any part of the listtext section so we don't call AddText() on the "code page escape" character and therefore we don't clear FIgnoreChars until we move on to the next RTF section. <br />
<br />
For example KMemo with a Bullet Point writes an RTF file that looks like this -<br />
<br />
{\listtext{\f0\fs20\cf1 \u8226\'3F\tab }} <br />
<br />
The "u8226" is a bullet point character (it is ignored by KMemo). The "'3F' is the "?" character, there in case the app cannot handle Unicode escapes.<br />
<br />
After that listtext block is processed FIgnore insists we still need to drop the first displayable character. So we loose first character of text following a bullet point.<br />
<br />
The Solution - Line 2712 at the end of procedure TKMemoRTFReader.ReadSpecialCharacter(...) -<br />
if SetIgnoreChars then <br />
FIgnoreChars := FIgnoreCharsAfterUnicode; <br />
<br />
becomes - <br />
<br />
if SetIgnoreChars and (FActiveState.Group <> RGUNKNOWN) then // DRB - April 2018<br />
FIgnoreChars := FIgnoreCharsAfterUnicode;<br />
<br />
=Installation =<br />
<br />
KMemo must be installed as part of the KControls Package. As is usual, its easy. You should be using a recent version of FPC and Lazarus. Download the Github vesion of KControls, unzip and put it somewhere suitable. Fire up Lazarus, click Packages, click "Open Pack File (.lpk)" and find the kcontrolslaz.lpk file in the kit you downloaded.<br />
<br />
Click Compile. <br />
<br />
Lazarus will do its install thing, shortly will ask you about rebuilding, answer yes, it will then shutdown. On startup, you will find an extra row of components at the far right, KMemo amongst them.<br />
<br />
If you find this as valuable a component as I did, I suggest you hit the link below and thank TK, it is one hell of an effort !<br />
<br />
= See Also =<br />
<br />
* [[KControls]] - the package KMemo is part of.<br />
* https://github.com/kryslt/KControls - Github repository.<br />
* [[RichMemo]] - an alternative package.<br />
* [[UTF8_strings_and_characters]]<br />
* How to hide / show the caret? - TBD<br />
<br />
[[Category:Components]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Lazarus_Examples_Window&diff=158156Lazarus Examples Window2024-01-28T00:33:10Z<p>Dbannon: Other Examples</p>
<hr />
<div><br />
<br />
<br />
The new Example Window that exists in Lazarus 3.0 and beyond is documented below.<br />
<br />
== For the end user ==<br />
===Introduction===<br />
A Lazarus user gets to the Example Window after clicking the button on the Project Wizard screen or from the Tools Menu, "Example Projects". In either case you see a window offering a list of known examples. The list can be filtered by key words or by category (double click between category checkboxes turns all off). In basic use, it shows you all the examples shipped with Lazarus. <br />
<br />
The Example Window will allow you to open, build (and edit) and run any example but note some have specific package requirements. When opened, the examples from the Lazarus Source code are copied to a (user writable) area in the Lazarus Config directory ([[pcp|PCP]]) because, on some systems Lazarus Source is in read-only disk space. <br />
<br />
Feel free to edit or make changes to examples in Lazarus Source, if you totally mess up, its easily refreshed from the Examples Window.<br />
<br />
===Examples in Packages===<br />
When you install a package that has compliant examples, they will also appear in the Lazarus Examples Window. If they don't appear, the most likely reason is that they do not have an example meta data file. See below.<br />
<br />
===Displaying your own Examples===<br />
If you have your own examples in one or more Lazarus projects, you can arrange to have them listed along with the built in examples supplied with Lazarus. Following the same rules as below, you would -<br />
* Ensure each example is in its own directory and is self contained.<br />
* Provide meta data files as described below. Perhaps set the category to something like "Private" ?<br />
* Add an entry to the ./examples/examples.txt file in your Lazarus Source directory. The entry, see the existing ones, should have a relative path from the top of the lazarus source directory to your meta data file. If you are using a packaged version of Lazarus then this examples.txt file might be in read only space and you will need administrative access to it.<br />
* Examples mentioned in examples.txt are always copied to a work area ensuring write access and allowing you to play without affecting the original.<br />
<br />
===FPC Examples===<br />
FPC also provides a number of examples relating to its packages and RTL. These examples are generally command line driven and don't have the Lazarus project information so are unsuited to working with the Lazarus Examples Window. (It might be nice to find a way to display them however.)<br />
<br />
== For the Developer ==<br />
<br />
=== Examples in the Lazarus Source ===<br />
<br />
You can add an example anywhere you like in the Lazarus Source Tree, there are lots under ~/examples but also many associated with different Lazarus sub system. A few rules apply -<br />
<br />
* The Example Name must be unique within Lazarus (when lowercased).<br />
* The example should be self contained within its own directory. That directory (and any sub directories) will be copied to the work area so don't assume particular files are "just up one dir".<br />
* The example should have an Example Metadata File, with a file name that corresponds to the project name and an extension of .ex-meta. The content of this file is JSON and must have a name, a category and should have a (multiline) description and keywords. See below. <br />
<br />
If you add a new example (or remove one) to the Lazarus Source Tree, the file ~/examples/examples.txt needs to be refreshed. Its plain text, edit it directly or generate a new one with this command (on a *nix )-<br />
<br />
$> find . -name "*.ex-meta" > examples/examples.txt<br />
<br />
=== Examples in Third Party Packages ===<br />
Again, you are free to place your package examples directory where you like but in order for the Lazarus Examples Window to find it, some things must be provided, note, different rules than above !<br />
<br />
* The package must be currently installed in Lazarus (check ? see if its listed in ([[pcp|PCP]])/staticpackages.inc)<br />
* Your example should be in a directory of its own. You should not put more than one example in the same directory.<br />
* In the case of Package Examples, the example directory is not moved to a work area so it can (but perhaps should not) be dependent on local, relative path files.<br />
* It must have a Metadata File, see below.<br />
* The Package file, that is the .lpk file must have entry such as <ExamplesDirectory Value="../demo"/>, typically just below the Author item. The value is the relative path from the .lpk file to a directory containing your example or examples. eg -<br />
[MyPackage]<br />
[demo]<br />
mypackage_demo1.ex-meta<br />
....<br />
[demo2]<br />
mypackage_demo2.ex-meta<br />
....<br />
[package]<br />
mypackage.lpk<br />
<br />
(Personally, I would put demo1 in its own directory too but the above would work !)<br />
<br />
Note that, at present, Lazarus, when making a package, does NOT create the ExamplesDirectory entry, it will, but its a work in progress. If you want to test, manually adding that entry is the way to go right now.<br />
<br />
=== The Metadata File ===<br />
<br />
{{Note| If you add a new example (or remove one) to the Lazarus Source Tree, the file ~/examples/examples.txt needs to be refreshed. Its plain text, edit it directly or generate a new one with this command from the top level Lazarus directory (on a *nix )-<br />
<br />
$> find . -name "*.ex-meta" > examples/examples.txt<br />
}}<br />
<br />
All examples that appear in the Example Window have a json metadata file with an extension ".ex-meta" in the top level directory of the example (not the package). Without a valid metadata file, the example will not be listed in the Example Window. An individual example project file would look like this -<br />
<br />
<syntaxhighlight lang=text>{ <br />
"Laz_Hello" : {<br />
"Category" : "Beginner",<br />
"Keywords" : ["Lazarus", "Hello World", "TButton", "TMemo"],<br />
"Description" : "This might be your first Lazarus project, its the traditional Hello World. Two buttons have been dropped on the form, renamed and their captions set. A memo was then added and each button was double clicked and what they are expected to do was set."<br />
}</syntaxhighlight><br />
<br />
In this case, the example project files would be in a subdirectory called laz_hello, same spelling but all lowercase consistent with Lazarus. <br />
<br />
The three fields are free form text, there is no dictionary so, you are free to introduce both new Categories and new Key Words. Its desirable that we do not have too many Categories so, perhaps consider posting a message on the forum before using a new one ? At present, the following Categories are in use -<br />
* Beginner<br />
* General<br />
* TAChart<br />
* DBase<br />
* LazReport<br />
* ThirdParty<br />
<br />
There is a rough and ready GUI app to make, edit and importantly, validate metadata files available at https://github.com/davidbannon/ExampleMetaData .<br />
<br />
=== Can the IDE find the example ?===<br />
<br />
If the IDE does not find an example, some things to check for -<br />
<br />
* Does it have a metadata file ?<br />
* Is there a message about the metadata from console ?<br />
* If its a Lazarus Source example, is it mentioned in examples/examples.txt ?<br />
* if a Package Example, is it mentioned in ([[pcp|PCP]])/staticpackages.inc ? and in ([[pcp|PCP]])/packagefiles.xml ? (Unlike the examples.txt file, do not manually edit these files, they should be updated when a package is installed into the the IDE.)<br />
<br />
==Notes==<br />
The user can view the project content and, if they choose, build the project. Later, if the user users the Example Window to again open that project, they will be asked if they want to refresh it, doing so will erase any false edits they may have made.<br />
<br />
Important to note -<br />
* Example Projects will not be displayed in the Examples Window unless they have a valid metadata file. An error is dropped to the console if a metadata file with bad json is encountered.<br />
* The original copy of the Example is not altered when the Example Window is used to open an Example. So, play away !<br />
* New examples can, potentially be added via the normal Gitlab pull request system. <br />
* When examples are added to or removed from the Lazarus Source, changes need be made to the (eg) ~/lazarus_3_0/examples/examples.txt file. See the Metadata section.<br />
* Examples demonstrating aspects of third party packages are probably best added to that package rather than to Lazarus itself.<br />
<br />
== See also ==<br />
<br />
* https://forum.lazarus.freepascal.org/index.php/topic,57680.0.html<br />
* https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/37509<br />
* https://gitlab.com/dbannon/laz_examples - this is just a temp home.<br />
* https://gitlab.com/dbannon/laz_examples/-/tree/main/Utility/ExScanner - a tool for manipulating Example Projects in and around the Lazarus Src Tree. Probably past its use by date. Also has a framework to 'exercise' the Lazarus Examples Window in a stand alone, easier to manage environment.<br />
* https://github.com/davidbannon/ExampleMetaData - a very simple editor to make and validate the Example Meta Data files.<br />
* https://gitlab.com/freepascal.org/lazarus/lazarus/-/blob/main/components/exampleswindow/uexampledata.pas the unit making decisions about your examples.<br />
<br />
<br />
<br />
[[Category:Example_programs]]<br />
[[Category:Development]]<br />
[[Category:Proposals]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Lazarus_Examples_Window&diff=158138Lazarus Examples Window2024-01-26T04:03:10Z<p>Dbannon: update now its about release code.</p>
<hr />
<div><br />
<br />
<br />
The new Example Window that exists in Lazarus 3.0 and beyond is documented below.<br />
<br />
== For the end user ==<br />
<br />
A Lazarus user gets to the Example Window after clicking the button on the Project Wizard screen or from the Tools Menu, "Example Projects". In either case you see a window offering a list of known examples. The list can be filtered by key words or by category (double click between category checkboxes turns all off). In basic use, it shows you all the examples shipped with Lazarus. <br />
<br />
The Example Window will allow you to open, build (and edit) and run any example but note some have specific package requirements. When opened, the examples from the Lazarus Source code are copied to a (user writable) area in the Lazarus Config directory ([[pcp|PCP]]) because, on some systems Lazarus Source is in read-only disk space. <br />
<br />
Feel free to edit or make changes to examples in Lazarus Source, if you totally mess up, its easily refreshed from the Examples Window.<br />
<br />
== For the Developer ==<br />
<br />
=== Examples in the Lazarus Source ===<br />
<br />
You can add an example anywhere you like in the Lazarus Source Tree, there are lots under ~/examples but also many associated with different Lazarus sub system. A few rules apply -<br />
<br />
* The Example Name must be unique within Lazarus (when lowercased).<br />
* The example should be self contained within its own directory. That directory (and any sub directories) will be copied to the work area so don't assume particular files are "just up one dir".<br />
* The example should have an Example Metadata File, with a file name that corresponds to the project name and an extension of .ex-meta. The content of this file is JSON and must have a name, a category and should have a (multiline) description and keywords. See below. <br />
<br />
If you add a new example (or remove one) to the Lazarus Source Tree, the file ~/examples/examples.txt needs to be refreshed. Its plain text, edit it directly or generate a new one with this command (on a *nix )-<br />
<br />
$> find . -name "*.ex-meta" > examples/examples.txt<br />
<br />
=== Examples in Third Party Packages ===<br />
Again, you are free to place your package examples directory where you like but in order for the Lazarus Examples Window to find it, some things must be provided, note, different rules than above !<br />
<br />
* The package must be currently installed in Lazarus (check ? see if its listed in ([[pcp|PCP]])/staticpackages.inc)<br />
* Your example should be in a directory of its own. You should not put more than one example in the same directory.<br />
* In the case of Package Examples, the example directory is not moved to a work area so it can (but perhaps should not) be dependent on local, relative path files.<br />
* It must have a Metadata File, see below.<br />
* The Package file, that is the .lpk file must have entry such as <ExamplesDirectory Value="../demo"/>, typically just below the Author item. The value is the relative path from the .lpk file to a directory containing your example or examples. eg -<br />
[MyPackage]<br />
[demo]<br />
mypackage_demo1.ex-meta<br />
....<br />
[demo2]<br />
mypackage_demo2.ex-meta<br />
....<br />
[package]<br />
mypackage.lpk<br />
<br />
(Personally, I would put demo1 in its own directory too but the above would work !)<br />
<br />
Note that, at present, Lazarus, when making a package, does NOT create the ExamplesDirectory entry, it will, but its a work in progress. If you want to test, manually adding that entry is the way to go right now.<br />
<br />
=== The Metadata File ===<br />
<br />
{{Note| If you add a new example (or remove one) to the Lazarus Source Tree, the file ~/examples/examples.txt needs to be refreshed. Its plain text, edit it directly or generate a new one with this command from the top level Lazarus directory (on a *nix )-<br />
<br />
$> find . -name "*.ex-meta" > examples/examples.txt<br />
}}<br />
<br />
All examples that appear in the Example Window have a json metadata file with an extension ".ex-meta" in the top level directory of the example (not the package). Without a valid metadata file, the example will not be listed in the Example Window. An individual example project file would look like this -<br />
<br />
<syntaxhighlight lang=text>{ <br />
"Laz_Hello" : {<br />
"Category" : "Beginner",<br />
"Keywords" : ["Lazarus", "Hello World", "TButton", "TMemo"],<br />
"Description" : "This might be your first Lazarus project, its the traditional Hello World. Two buttons have been dropped on the form, renamed and their captions set. A memo was then added and each button was double clicked and what they are expected to do was set."<br />
}</syntaxhighlight><br />
<br />
In this case, the example project files would be in a subdirectory called laz_hello, same spelling but all lowercase consistent with Lazarus. <br />
<br />
The three fields are free form text, there is no dictionary so, you are free to introduce both new Categories and new Key Words. Its desirable that we do not have too many Categories so, perhaps consider posting a message on the forum before using a new one ? At present, the following Categories are in use -<br />
* Beginner<br />
* General<br />
* TAChart<br />
* DBase<br />
* LazReport<br />
* ThirdParty<br />
<br />
There is a rough and ready GUI app to make, edit and importantly, validate metadata files available at https://github.com/davidbannon/ExampleMetaData .<br />
<br />
=== Can the IDE find the example ?===<br />
<br />
If the IDE does not find an example, some things to check for -<br />
<br />
* Does it have a metadata file ?<br />
* Is there a message about the metadata from console ?<br />
* If its a Lazarus Source example, is it mentioned in examples/examples.txt ?<br />
* if a Package Example, is it mentioned in ([[pcp|PCP]])/staticpackages.inc ? and in ([[pcp|PCP]])/packagefiles.xml ? (Unlike the examples.txt file, do not manually edit these files, they should be updated when a package is installed into the the IDE.)<br />
<br />
==Notes==<br />
The user can view the project content and, if they choose, build the project. Later, if the user users the Example Window to again open that project, they will be asked if they want to refresh it, doing so will erase any false edits they may have made.<br />
<br />
Important to note -<br />
* Example Projects will not be displayed in the Examples Window unless they have a valid metadata file. An error is dropped to the console if a metadata file with bad json is encountered.<br />
* The original copy of the Example is not altered when the Example Window is used to open an Example. So, play away !<br />
* New examples can, potentially be added via the normal Gitlab pull request system. <br />
* When examples are added to or removed from the Lazarus Source, changes need be made to the (eg) ~/lazarus_3_0/examples/examples.txt file. See the Metadata section.<br />
* Examples demonstrating aspects of third party packages are probably best added to that package rather than to Lazarus itself.<br />
<br />
== See also ==<br />
<br />
* https://forum.lazarus.freepascal.org/index.php/topic,57680.0.html<br />
* https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/37509<br />
* https://gitlab.com/dbannon/laz_examples - this is just a temp home.<br />
* https://gitlab.com/dbannon/laz_examples/-/tree/main/Utility/ExScanner - a tool for manipulating Example Projects in and around the Lazarus Src Tree. Probably past its use by date. Also has a framework to 'exercise' the Lazarus Examples Window in a stand alone, easier to manage environment.<br />
* https://github.com/davidbannon/ExampleMetaData - a very simple editor to make and validate the Example Meta Data files.<br />
* https://gitlab.com/freepascal.org/lazarus/lazarus/-/blob/main/components/exampleswindow/uexampledata.pas the unit making decisions about your examples.<br />
<br />
<br />
<br />
[[Category:Example_programs]]<br />
[[Category:Development]]<br />
[[Category:Proposals]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Lazarus_Examples_Window&diff=158137Lazarus Examples Window2024-01-26T03:59:35Z<p>Dbannon: update now its about release code.</p>
<hr />
<div><br />
<br />
<br />
The new Example Window that exists in Lazarus 3.0 and beyond is documented below.<br />
<br />
== For the end user ==<br />
<br />
A Lazarus user gets to the Example Window after clicking the button on the Project Wizard screen or from the Tools Menu, "Example Projects". In either case you see a window offering a list of known examples. The list can be filtered by key words or by category (double click between category checkboxes turns all off). In basic use, it shows you all the examples shipped with Lazarus. <br />
<br />
The Example Window will allow you to open, build (and edit) and run any example but note some have specific package requirements. When opened, the examples from the Lazarus Source code are copied to a (user writable) area in the Lazarus Config directory ([[pcp|PCP]]) because, on some systems Lazarus Source is in read-only disk space. <br />
<br />
Feel free to edit or make changes to examples in Lazarus Source, if you totally mess up, its easily refreshed from the Examples Window.<br />
<br />
== For the Developer ==<br />
<br />
=== Examples in the Lazarus Source ===<br />
<br />
You can add an example anywhere you like in the Lazarus Source Tree, there are lots under ~/examples but also many associated with different Lazarus sub system. A few rules apply -<br />
<br />
* The Example Name must be unique within Lazarus (when lowercased).<br />
* The example should be self contained within its own directory. That directory (and any sub directories) will be copied to the work area so don't assume particular files are "just up one dir".<br />
* The example should have an Example Metadata File, with a file name that corresponds to the project name and an extension of .ex-meta. The content of this file is JSON and must have a name, a category and should have a (multiline) description and keywords. See below. <br />
<br />
If you add a new example (or remove one) to the Lazarus Source Tree, the file ~/examples/examples.txt needs to be refreshed. Its plain text, edit it directly or generate a new one with this command (on a *nix )-<br />
<br />
$> find . -name "*.ex-meta" > examples/examples.txt<br />
<br />
=== Examples in Third Party Packages ===<br />
Again, you are free to place your package examples directory where you like but in order for the Lazarus Examples Window to find it, some things must be provided, note, different rules than above !<br />
<br />
* The package must be currently installed in Lazarus (check ? see if its listed in ([[pcp|PCP]])/staticpackages.inc)<br />
* Your example should be in a directory of its own. You should not put more than one example in the same directory.<br />
* In the case of Package Examples, the example directory is not moved to a work area so it can (but perhaps should not) be dependent on local, relative path files.<br />
* It must have a Metadata File, see below.<br />
* The Package file, that is the .lpk file must have entry such as <ExamplesDirectory Value="../demo"/>, typically just below the Author item. The value is the relative path from the .lpk file to a directory containing your example or examples. eg -<br />
[MyPackage]<br />
[demo]<br />
mypackage_demo1.ex-meta<br />
....<br />
[demo2]<br />
mypackage_demo2.ex-meta<br />
....<br />
[package]<br />
mypackage.lpk<br />
<br />
(Personally, I would put demo1 in its own directory too but the above would work !)<br />
<br />
Note that, at present, Lazarus, when making a package, does NOT create the ExamplesDirectory entry, it will, but its a work in progress. If you want to test, manually adding that entry is the way to go right now.<br />
<br />
=== The Metadata File ===<br />
<br />
{{Note| If you add a new example (or remove one) to the Lazarus Source Tree, the file ~/examples/examples.txt needs to be refreshed. Its plain text, edit it directly or generate a new one with this command from the top level Lazarus directory (on a *nix )-<br />
<br />
$> find . -name "*.ex-meta" > examples/examples.txt<br />
}}<br />
<br />
All examples that appear in the Example Window have a json metadata file with an extension ".ex-meta" in the top level directory of the example (not the package). Without a valid metadata file, the example will not be listed in the Example Window. An individual example project file would look like this -<br />
<br />
<syntaxhighlight lang=text>{ <br />
"Laz_Hello" : {<br />
"Category" : "Beginner",<br />
"Keywords" : ["Lazarus", "Hello World", "TButton", "TMemo"],<br />
"Description" : "This might be your first Lazarus project, its the traditional Hello World. Two buttons have been dropped on the form, renamed and their captions set. A memo was then added and each button was double clicked and what they are expected to do was set."<br />
}</syntaxhighlight><br />
<br />
In this case, the example project files would be in a subdirectory called laz_hello, same spelling but all lowercase consistent with Lazarus. <br />
<br />
The three fields are free form text, there is no dictionary so, you are free to introduce both new Categories and new Key Words. Its desirable that we do not have too many Categories so, perhaps consider posting a message on the forum before using a new one ? At present, the following Categories are in use -<br />
* Beginner<br />
* General<br />
* TAChart<br />
* DBase<br />
* LazReport<br />
* ThirdParty<br />
<br />
There is a rough and ready GUI app to make, edit and importantly, validate metadata files available at https://github.com/davidbannon/ExampleMetaData .<br />
<br />
== Can the IDE find the example ?==<br />
<br />
If the IDE does not find an example, some things to check for -<br />
<br />
* Does it have a metadata file ?<br />
* Is there a message about the metadata from console ?<br />
* If its a Lazarus Source example, is it mentioned in examples/examples.txt ?<br />
* if a Package Example, is it mentioned in ([[pcp|PCP]])/staticpackages.inc ? and in ([[pcp|PCP]])/packagefiles.xml ?<br />
<br />
==Notes==<br />
The user can view the project content and, if they choose, build the project. Later, if the user users the Example Window to again open that project, they will be asked if they want to refresh it, doing so will erase any false edits they may have made.<br />
<br />
Important to note -<br />
* Example Projects will not be displayed in the Examples Window unless they have a valid metadata file. An error is dropped to the console if a metadata file with bad json is encountered.<br />
* The original copy of the Example is not altered when the Example Window is used to open an Example. So, play away !<br />
* New examples can, potentially be added via the normal Gitlab pull request system. <br />
* When examples are added to or removed from the Lazarus Source, changes need be made to the (eg) ~/lazarus_3_0/examples/examples.txt file. See the Metadata section.<br />
* Examples demonstrating aspects of third party packages are probably best added to that package rather than to Lazarus itself.<br />
<br />
== See also ==<br />
<br />
* https://forum.lazarus.freepascal.org/index.php/topic,57680.0.html<br />
* https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/37509<br />
* https://gitlab.com/dbannon/laz_examples - this is just a temp home.<br />
* https://gitlab.com/dbannon/laz_examples/-/tree/main/Utility/ExScanner - a tool for manipulating Example Projects in and around the Lazarus Src Tree. Probably past its use by date. Also has a framework to 'exercise' the Lazarus Examples Window in a stand alone, easier to manage environment.<br />
* https://github.com/davidbannon/ExampleMetaData - a very simple editor to make and validate the Example Meta Data files.<br />
* https://gitlab.com/freepascal.org/lazarus/lazarus/-/blob/main/components/exampleswindow/uexampledata.pas the unit making decisions about your examples.<br />
<br />
<br />
<br />
[[Category:Example_programs]]<br />
[[Category:Development]]<br />
[[Category:Proposals]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Lazarus_Examples_Window&diff=158136Lazarus Examples Window2024-01-26T03:58:02Z<p>Dbannon: /* The Metadata File */</p>
<hr />
<div><br />
<br />
<br />
The new Example Window that exists in Lazarus 3.0 and beyond is documented below.<br />
<br />
== For the end user ==<br />
<br />
A Lazarus user gets to the Example Window after clicking the button on the Project Wizard screen or from the Tools Menu, "Example Projects". In either case you see a window offering a list of known examples. The list can be filtered by key words or by category (double click between category checkboxes turns all off). In basic use, it shows you all the examples shipped with Lazarus. <br />
<br />
The Example Window will allow you to open, build (and edit) and run any example but note some have specific package requirements. When opened, the examples from the Lazarus Source code are copied to a (user writable) area in the Lazarus Config directory ([[pcp|PCP]]) because, on some systems Lazarus Source is in read-only disk space. <br />
<br />
Feel free to edit or make changes to examples in Lazarus Source, if you totally mess up, its easily refreshed from the Examples Window.<br />
<br />
== For the Developer ==<br />
<br />
=== Examples in the Lazarus Source ===<br />
<br />
You can add an example anywhere you like in the Lazarus Source Tree, there are lots under ~/examples but also many associated with different Lazarus sub system. A few rules apply -<br />
<br />
* The Example Name must be unique within Lazarus (when lowercased).<br />
* The example should be self contained within its own directory. That directory (and any sub directories) will be copied to the work area so don't assume particular files are "just up one dir".<br />
* The example should have an Example Metadata File, with a file name that corresponds to the project name and an extension of .ex-meta. The content of this file is JSON and must have a name, a category and should have a (multiline) description and keywords. See below. <br />
<br />
If you add a new example (or remove one) to the Lazarus Source Tree, the file ~/examples/examples.txt needs to be refreshed. Its plain text, edit it directly or generate a new one with this command (on a *nix )-<br />
<br />
$> find . -name "*.ex-meta" > examples/examples.txt<br />
<br />
=== Examples in Third Party Packages ===<br />
Again, you are free to place your package examples directory where you like but in order for the Lazarus Examples Window to find it, some things must be provided, note, different rules than above !<br />
<br />
* The package must be currently installed in Lazarus (check ? see if its listed in ([[pcp|PCP]])/staticpackages.inc)<br />
* Your example should be in a directory of its own. You should not put more than one example in the same directory.<br />
* In the case of Package Examples, the example directory is not moved to a work area so it can (but perhaps should not) be dependent on local, relative path files.<br />
* It must have a Metadata File, see below.<br />
* The Package file, that is the .lpk file must have entry such as <ExamplesDirectory Value="../demo"/>, typically just below the Author item. The value is the relative path from the .lpk file to a directory containing your example or examples. eg -<br />
[MyPackage]<br />
[demo]<br />
mypackage_demo1.ex-meta<br />
....<br />
[demo2]<br />
mypackage_demo2.ex-meta<br />
....<br />
[package]<br />
mypackage.lpk<br />
<br />
(Personally, I would put demo1 in its own directory too but the above would work !)<br />
<br />
'''''Note that, at present, Lazarus, when making a package, does NOT create the ExamplesDirectory entry, it will, but its a work in progress. If you want to test, manually adding that entry is the way to go right now.'''''<br />
<br />
=== The Metadata File ===<br />
<br />
{{Note| If you add a new example (or remove one) to the Lazarus Source Tree, the file ~/examples/examples.txt needs to be refreshed. Its plain text, edit it directly or generate a new one with this command from the top level Lazarus directory (on a *nix )-<br />
<br />
$> find . -name "*.ex-meta" > examples/examples.txt<br />
}}<br />
<br />
All examples that appear in the Example Window have a json metadata file with an extension ".ex-meta" in the top level directory of the example (not the package). Without a valid metadata file, the example will not be listed in the Example Window. An individual example project file would look like this -<br />
<br />
<syntaxhighlight lang=text>{ <br />
"Laz_Hello" : {<br />
"Category" : "Beginner",<br />
"Keywords" : ["Lazarus", "Hello World", "TButton", "TMemo"],<br />
"Description" : "This might be your first Lazarus project, its the traditional Hello World. Two buttons have been dropped on the form, renamed and their captions set. A memo was then added and each button was double clicked and what they are expected to do was set."<br />
}</syntaxhighlight><br />
<br />
In this case, the example project files would be in a subdirectory called laz_hello, same spelling but all lowercase consistent with Lazarus. <br />
<br />
The three fields are free form text, there is no dictionary so, you are free to introduce both new Categories and new Key Words. Its desirable that we do not have too many Categories so, perhaps consider posting a message on the forum before using a new one ? At present, the following Categories are in use -<br />
* Beginner<br />
* General<br />
* TAChart<br />
* DBase<br />
* LazReport<br />
* ThirdParty<br />
<br />
There is a rough and ready GUI app to make, edit and importantly, validate metadata files available at https://github.com/davidbannon/ExampleMetaData .<br />
<br />
== Can the IDE find the example ?==<br />
<br />
If the IDE does not find an example, some things to check for -<br />
<br />
* Does it have a metadata file ?<br />
* Is there a message about the metadata from console ?<br />
* If its a Lazarus Source example, is it mentioned in examples/examples.txt ?<br />
* if a Package Example, is it mentioned in ([[pcp|PCP]])/staticpackages.inc ? and in ([[pcp|PCP]])/packagefiles.xml ?<br />
<br />
==Notes==<br />
The user can view the project content and, if they choose, build the project. Later, if the user users the Example Window to again open that project, they will be asked if they want to refresh it, doing so will erase any false edits they may have made.<br />
<br />
Important to note -<br />
* Example Projects will not be displayed in the Examples Window unless they have a valid metadata file. An error is dropped to the console if a metadata file with bad json is encountered.<br />
* The original copy of the Example is not altered when the Example Window is used to open an Example. So, play away !<br />
* New examples can, potentially be added via the normal Gitlab pull request system. <br />
* When examples are added to or removed from the Lazarus Source, changes need be made to the (eg) ~/lazarus_3_0/examples/examples.txt file. See the Metadata section.<br />
* Examples demonstrating aspects of third party packages are probably best added to that package rather than to Lazarus itself.<br />
<br />
== See also ==<br />
<br />
* https://forum.lazarus.freepascal.org/index.php/topic,57680.0.html<br />
* https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/37509<br />
* https://gitlab.com/dbannon/laz_examples - this is just a temp home.<br />
* https://gitlab.com/dbannon/laz_examples/-/tree/main/Utility/ExScanner - a tool for manipulating Example Projects in and around the Lazarus Src Tree. Probably past its use by date. Also has a framework to 'exercise' the Lazarus Examples Window in a stand alone, easier to manage environment.<br />
* https://github.com/davidbannon/ExampleMetaData - a very simple editor to make and validate the Example Meta Data files.<br />
* https://gitlab.com/freepascal.org/lazarus/lazarus/-/blob/main/components/exampleswindow/uexampledata.pas the unit making decisions about your examples.<br />
<br />
<br />
<br />
[[Category:Example_programs]]<br />
[[Category:Development]]<br />
[[Category:Proposals]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Lazarus_Examples_Window&diff=158135Lazarus Examples Window2024-01-26T03:51:26Z<p>Dbannon: update now its about release code.</p>
<hr />
<div><br />
<br />
<br />
The new Example Window that exists in Lazarus 3.0 and beyond is documented below.<br />
<br />
== For the end user ==<br />
<br />
A Lazarus user gets to the Example Window after clicking the button on the Project Wizard screen or from the Tools Menu, "Example Projects". In either case you see a window offering a list of known examples. The list can be filtered by key words or by category (double click between category checkboxes turns all off). In basic use, it shows you all the examples shipped with Lazarus. <br />
<br />
The Example Window will allow you to open, build (and edit) and run any example but note some have specific package requirements. When opened, the examples from the Lazarus Source code are copied to a (user writable) area in the Lazarus Config directory ([[pcp|PCP]]) because, on some systems Lazarus Source is in read-only disk space. <br />
<br />
Feel free to edit or make changes to examples in Lazarus Source, if you totally mess up, its easily refreshed from the Examples Window.<br />
<br />
== For the Developer ==<br />
<br />
=== Examples in the Lazarus Source ===<br />
<br />
You can add an example anywhere you like in the Lazarus Source Tree, there are lots under ~/examples but also many associated with different Lazarus sub system. A few rules apply -<br />
<br />
* The Example Name must be unique within Lazarus (when lowercased).<br />
* The example should be self contained within its own directory. That directory (and any sub directories) will be copied to the work area so don't assume particular files are "just up one dir".<br />
* The example should have an Example Metadata File, with a file name that corresponds to the project name and an extension of .ex-meta. The content of this file is JSON and must have a name, a category and should have a (multiline) description and keywords. See below. <br />
<br />
If you add a new example (or remove one) to the Lazarus Source Tree, the file ~/examples/examples.txt needs to be refreshed. Its plain text, edit it directly or generate a new one with this command (on a *nix )-<br />
<br />
$> find . -name "*.ex-meta" > examples/examples.txt<br />
<br />
=== Examples in Third Party Packages ===<br />
Again, you are free to place your package examples directory where you like but in order for the Lazarus Examples Window to find it, some things must be provided, note, different rules than above !<br />
<br />
* The package must be currently installed in Lazarus (check ? see if its listed in ([[pcp|PCP]])/staticpackages.inc)<br />
* Your example should be in a directory of its own. You should not put more than one example in the same directory.<br />
* In the case of Package Examples, the example directory is not moved to a work area so it can (but perhaps should not) be dependent on local, relative path files.<br />
* It must have a Metadata File, see below.<br />
* The Package file, that is the .lpk file must have entry such as <ExamplesDirectory Value="../demo"/>, typically just below the Author item. The value is the relative path from the .lpk file to a directory containing your example or examples. eg -<br />
[MyPackage]<br />
[demo]<br />
mypackage_demo1.ex-meta<br />
....<br />
[demo2]<br />
mypackage_demo2.ex-meta<br />
....<br />
[package]<br />
mypackage.lpk<br />
<br />
(Personally, I would put demo1 in its own directory too but the above would work !)<br />
<br />
'''''Note that, at present, Lazarus, when making a package, does NOT create the ExamplesDirectory entry, it will, but its a work in progress. If you want to test, manually adding that entry is the way to go right now.'''''<br />
<br />
== The Metadata File ==<br />
<br />
All examples that appear in the Example Window have a json metadata file with an extension ".ex-meta" in the top level directory of the example (not the package). Without a valid metadata file, the example will not be listed in the Example Window. An individual example project file would look like this -<br />
<br />
<syntaxhighlight lang=text>{ <br />
"Laz_Hello" : {<br />
"Category" : "Beginner",<br />
"Keywords" : ["Lazarus", "Hello World", "TButton", "TMemo"],<br />
"Description" : "This might be your first Lazarus project, its the traditional Hello World. Two buttons have been dropped on the form, renamed and their captions set. A memo was then added and each button was double clicked and what they are expected to do was set."<br />
}</syntaxhighlight><br />
<br />
In this case, the example project files would be in a subdirectory called laz_hello, same spelling but all lowercase consistent with Lazarus. <br />
<br />
The three fields are free form text, there is no dictionary so, you are free to introduce both new Categories and new Key Words. Its desirable that we do not have too many Categories so, perhaps consider posting a message on the forum before using a new one ? At present, the following Categories are in use -<br />
* Beginner<br />
* General<br />
* TAChart<br />
* DBase<br />
* LazReport<br />
* ThirdParty<br />
<br />
There is a rough and ready GUI app to make, edit and importantly, validate metadata files available at https://github.com/davidbannon/ExampleMetaData .<br />
<br />
== Can the IDE find the example ?==<br />
<br />
If the IDE does not find an example, some things to check for -<br />
<br />
* Does it have a metadata file ?<br />
* Is there a message about the metadata from console ?<br />
* If its a Lazarus Source example, is it mentioned in examples/examples.txt ?<br />
* if a Package Example, is it mentioned in ([[pcp|PCP]])/staticpackages.inc ? and in ([[pcp|PCP]])/packagefiles.xml ?<br />
<br />
==Notes==<br />
The user can view the project content and, if they choose, build the project. Later, if the user users the Example Window to again open that project, they will be asked if they want to refresh it, doing so will erase any false edits they may have made.<br />
<br />
Important to note -<br />
* Example Projects will not be displayed in the Examples Window unless they have a valid metadata file. An error is dropped to the console if a metadata file with bad json is encountered.<br />
* The original copy of the Example is not altered when the Example Window is used to open an Example. So, play away !<br />
* New examples can, potentially be added via the normal Gitlab pull request system. <br />
* When examples are added to or removed from the Lazarus Source, changes need be made to the (eg) ~/lazarus_3_0/examples/examples.txt file. See the Metadata section.<br />
* Examples demonstrating aspects of third party packages are probably best added to that package rather than to Lazarus itself.<br />
<br />
== See also ==<br />
<br />
* https://forum.lazarus.freepascal.org/index.php/topic,57680.0.html<br />
* https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/37509<br />
* https://gitlab.com/dbannon/laz_examples - this is just a temp home.<br />
* https://gitlab.com/dbannon/laz_examples/-/tree/main/Utility/ExScanner - a tool for manipulating Example Projects in and around the Lazarus Src Tree. Probably past its use by date. Also has a framework to 'exercise' the Lazarus Examples Window in a stand alone, easier to manage environment.<br />
* https://github.com/davidbannon/ExampleMetaData - a very simple editor to make and validate the Example Meta Data files.<br />
* https://gitlab.com/freepascal.org/lazarus/lazarus/-/blob/main/components/exampleswindow/uexampledata.pas the unit making decisions about your examples.<br />
<br />
<br />
<br />
[[Category:Example_programs]]<br />
[[Category:Development]]<br />
[[Category:Proposals]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Lazarus_Examples_Window&diff=158134Lazarus Examples Window2024-01-26T03:47:33Z<p>Dbannon: update now its about release code.</p>
<hr />
<div><br />
<br />
<br />
The new Example Window that exists in Lazarus 3.0 and beyond is documented below.<br />
<br />
== For the end user ==<br />
<br />
A Lazarus user gets to the Example Window after clicking the button on the Project Wizard screen or from the Tools Menu, "Example Projects". In either case you see a window offering a list of known examples. The list can be filtered by key words or by category (double click between category checkboxes turns all off). In basic use, it shows you all the examples shipped with Lazarus. <br />
<br />
The Example Window will allow you to open, build (and edit) and run any example but note some have specific package requirements. When opened, the examples from the Lazarus Source code are copied to a (user writable) area in the Lazarus Config directory ([[pcp|PCP]]) because, on some systems Lazarus Source is in read-only disk space. <br />
<br />
Feel free to edit or make changes to examples in Lazarus Source, if you totally mess up, its easily refreshed from the Examples Window.<br />
<br />
== For the Developer ==<br />
<br />
=== Examples in the Lazarus Source ===<br />
<br />
You can add an example anywhere you like in the Lazarus Source Tree, there are lots under ~/examples but also many associated with different Lazarus sub system. A few rules apply -<br />
<br />
* The Example Name must be unique within Lazarus (when lowercased).<br />
* The example should be self contained within its own directory. That directory (and any sub directories) will be copied to the work area so don't assume particular files are "just up one dir".<br />
* The example should have an Example Metadata File, with a file name that corresponds to the project name and an extension of .ex-meta. The content of this file is JSON and must have a name, a category and should have a (multiline) description and keywords. See below. <br />
<br />
If you add a new example (or remove one) to the Lazarus Source Tree, the file ~/examples/examples.txt needs to be refreshed. Its plain text, edit it directly or generate a new one with this command (on a *nix )-<br />
<br />
$> find . -name "*.ex-meta" > examples/examples.txt<br />
<br />
=== Examples in Third Party Packages ===<br />
Again, you are free to place your package examples directory where you like but in order for the Lazarus Examples Window to find it, some things must be provided, note, different rules than above !<br />
<br />
* The package must be currently installed in Lazarus (check ? see if its listed in ([[pcp|PCP]])/staticpackages.inc)<br />
* Your example should be in a directory of its own. You should not put more than one example in the same directory.<br />
* In the case of Package Examples, the example directory is not moved to a work area so it can (but perhaps should not) be dependent on local, relative path files.<br />
* It must have a Metadata File, see below.<br />
* The Package file, that is the .lpk file must have entry such as <ExamplesDirectory Value="../demo"/>, typically just below the Author item. The value is the relative path from the .lpk file to a directory containing your example or examples. eg -<br />
[MyPackage]<br />
[demo]<br />
mypackage_demo1.ex-meta<br />
....<br />
[demo2]<br />
mypackage_demo2.ex-meta<br />
....<br />
[package]<br />
mypackage.lpk<br />
<br />
(Personally, I would put demo1 in its own directory too but the above would work !)<br />
<br />
'''''Note that, at present, Lazarus, when making a package, does NOT create the ExamplesDirectory entry, it will, but its a work in progress. If you want to test, manually adding that entry is the way to go right now.'''''<br />
<br />
== The Metadata File ==<br />
<br />
All examples that appear in the Example Window have a json metadata file with an extension ".ex-meta" in the top level directory of the example (not the package). Without a valid metadata file, the example will not be listed in the Example Window. An individual example project file would look like this -<br />
<br />
<syntaxhighlight lang=text>{ <br />
"Laz_Hello" : {<br />
"Category" : "Beginner",<br />
"Keywords" : ["Lazarus", "Hello World", "TButton", "TMemo"],<br />
"Description" : "This might be your first Lazarus project, its the traditional Hello World. Two buttons have been dropped on the form, renamed and their captions set. A memo was then added and each button was double clicked and what they are expected to do was set."<br />
}</syntaxhighlight><br />
<br />
In this case, the example project files would be in a subdirectory called laz_hello, same spelling but all lowercase consistent with Lazarus. <br />
<br />
The three fields are free form text, there is no dictionary so, you are free to introduce both new Categories and new Key Words. Its desirable that we do not have too many Categories so, perhaps consider posting a message on the forum before using a new one ? At present, the following Categories are in use -<br />
* Beginner<br />
* General<br />
* TAChart<br />
* DBase<br />
* LazReport<br />
* ThirdParty<br />
<br />
There is a rough and ready GUI app to make, edit and importantly, validate metadata files available at https://github.com/davidbannon/ExampleMetaData .<br />
<br />
== Can the IDE find the example ?==<br />
<br />
If the IDE does not find an example, some things to check for -<br />
<br />
* Does it have a metadata file ?<br />
* Is there a message about the metadata from console ?<br />
* If its a Lazarus Source example, is it mentioned in examples/examples.txt ?<br />
* if a Package Example, is it mentioned in ([[pcp|PCP]])/staticpackages.inc ? and in ([[pcp|PCP]])/packagefiles.xml ?<br />
<br />
==Notes==<br />
The user can view the project content and, if they choose, build the project. Later, if the user users the Example Window to again open that project, they will be asked if they want to refresh it, doing so will erase any false edits they may have made.<br />
<br />
Important to note -<br />
* Example Projects will not be displayed in the Examples Window unless they have a valid metadata file. An error is dropped to the console if a metadata file with bad json is encountered.<br />
* The original copy of the Example is not altered when the Example Window is used to open an Example. So, play away !<br />
* New examples can, potentially be added via the normal Gitlab pull request system. <br />
* Examples demonstrating aspects of third party packages are probably best added to that package rather than to Lazarus itself.<br />
<br />
== See also ==<br />
<br />
* https://forum.lazarus.freepascal.org/index.php/topic,57680.0.html<br />
* https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/37509<br />
* https://gitlab.com/dbannon/laz_examples - this is just a temp home.<br />
* https://gitlab.com/dbannon/laz_examples/-/tree/main/Utility/ExScanner - a tool for manipulating Example Projects in and around the Lazarus Src Tree. Probably past its use by date. Also has a framework to 'exercise' the Lazarus Examples Window in a stand alone, easier to manage environment.<br />
* https://github.com/davidbannon/ExampleMetaData - a very simple editor to make and validate the Example Meta Data files.<br />
* https://gitlab.com/freepascal.org/lazarus/lazarus/-/blob/main/components/exampleswindow/uexampledata.pas the unit making decisions about your examples.<br />
<br />
<br />
<br />
[[Category:Example_programs]]<br />
[[Category:Development]]<br />
[[Category:Proposals]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Lazarus_Examples_Window&diff=158133Lazarus Examples Window2024-01-26T03:37:03Z<p>Dbannon: update now its about release code.</p>
<hr />
<div><br />
<br />
<br />
The new Example Window that exists in Lazarus 3.0 and beyond is documented below. It is based on this forum thread - https://forum.lazarus.freepascal.org/index.php/topic,57680.0.html and is by no means finalized.<br />
<br />
== For the end user ==<br />
<br />
A Lazarus user gets to the Example Window after clicking the button on the Project Wizard screen or from the Tools Menu, "Example Projects". In either case they see a window offering a list of known examples. The list can be filtered by key words or by category (double click between category checkboxes turns all off). In basic use, it shows you all the examples shipped with Lazarus. <br />
<br />
The Example Window will allow the user to open, build (and edit) and run any example but note some have specific package requirements. Lazarus Source examples can be refreshed if necessary. When opened, the examples from the Lazarus Source code are copied to a (user writable) area in the Lazarus Config directory ([[pcp|PCP]]) because, on some systems Lazarus Source is in read-only disk space. Further, this permits easy refresh of an example if the user makes changes.<br />
<br />
== For the Developer ==<br />
<br />
=== Examples in the Lazarus Source ===<br />
<br />
You can add an example anywhere you like in the Lazarus Source Tree, there are lots under ~/examples but also many associated with different Lazarus sub system. A few rules apply -<br />
<br />
* The Example Name must be unique within Lazarus (when lowercased).<br />
* The example should be self contained within its own directory. That directory (and any sub directories) will be copied to the work area so don't assume particular files are "just up one dir".<br />
* The example should have an Example Metadata File, with a file name that corresponds to the project name and an extension of .ex-meta. The content of this file is JSON and must have a name, a category and should have a (multiline) description and keywords. See below. <br />
<br />
If you add a new example (or remove one) to the Lazarus Source Tree, the file ~/examples/examples.txt needs to be refreshed. Its plain text, edit it directly or generate a new one with this command (on a *nix )-<br />
<br />
$> find . -name "*.ex-meta" > examples/examples.txt<br />
<br />
=== Examples in Third Party Packages ===<br />
Again, you are free to place your package examples directory where you like but in order for the Lazarus Examples Window to find it, some things must be provided, note, different rules than above !<br />
<br />
* The package must be currently installed in Lazarus (check ? see if its listed in ([[pcp|PCP]])/staticpackages.inc)<br />
* Your example should be in a directory of its own. You should not put more than one example in the same directory.<br />
* In the case of Package Examples, the example directory is not moved to a work area so it can (but perhaps should not) be dependent on local, relative path files.<br />
* It must have a Metadata File, see below.<br />
* The Package file, that is the .lpk file must have entry such as <ExamplesDirectory Value="../demo"/>, typically just below the Author item. The value is the relative path from the .lpk file to a directory containing your example or examples. eg -<br />
[MyPackage]<br />
[demo]<br />
mypackage_demo1.ex-meta<br />
....<br />
[demo2]<br />
mypackage_demo2.ex-meta<br />
....<br />
[package]<br />
mypackage.lpk<br />
<br />
(Personally, I would put demo1 in its own directory too but the above would work !)<br />
<br />
'''''Note that, at present, Lazarus, when making a package, does NOT create the ExamplesDirectory entry, it will, but its a work in progress. If you want to test, manually adding that entry is the way to go right now.'''''<br />
<br />
== The Metadata File ==<br />
<br />
All examples that appear in the Example Window have a json metadata file with an extension ".ex-meta" in the top level directory of the example (not the package). Without a valid metadata file, the example will not be listed in the Example Window. An individual example project file would look like this -<br />
<br />
<syntaxhighlight lang=text>{ <br />
"Laz_Hello" : {<br />
"Category" : "Beginner",<br />
"Keywords" : ["Lazarus", "Hello World", "TButton", "TMemo"],<br />
"Description" : "This might be your first Lazarus project, its the traditional Hello World. Two buttons have been dropped on the form, renamed and their captions set. A memo was then added and each button was double clicked and what they are expected to do was set."<br />
}</syntaxhighlight><br />
<br />
In this case, the example project files would be in a subdirectory called laz_hello, same spelling but all lowercase consistent with Lazarus. <br />
<br />
The three fields are free form text, there is no dictionary so, you are free to introduce both new Categories and new Key Words. Its desirable that we do not have too many Categories so, perhaps consider posting a message on the forum before using a new one ? At present, the following Categories are in use -<br />
* Beginner<br />
* General<br />
* TAChart<br />
* DBase<br />
* LazReport<br />
* ThirdParty<br />
<br />
There is a rough and ready GUI app to make, edit and importantly, validate metadata files available at https://github.com/davidbannon/ExampleMetaData .<br />
<br />
== Can the IDE find the example ?==<br />
<br />
If the IDE does not find an example, some things to check for -<br />
<br />
* Does it have a metadata file ?<br />
* Is there a message about the metadata from console ?<br />
* If its a Lazarus Source example, is it mentioned in examples/examples.txt ?<br />
* if a Package Example, is it mentioned in ([[pcp|PCP]])/staticpackages.inc ? and in ([[pcp|PCP]])/packagefiles.xml ?<br />
<br />
==Notes==<br />
The user can view the project content and, if they choose, build the project. Later, if the user users the Example Window to again open that project, they will be asked if they want to refresh it, doing so will erase any false edits they may have made.<br />
<br />
Important to note -<br />
* Example Projects will not be displayed in the Examples Window unless they have a valid metadata file. An error is dropped to the console if a metadata file with bad json is encountered.<br />
* The original copy of the Example is not altered when the Example Window is used to open an Example. So, play away !<br />
* New examples can, potentially be added via the normal Gitlab pull request system. <br />
* Examples demonstrating aspects of third party packages are probably best added to that package rather than to Lazarus itself.<br />
<br />
== See also ==<br />
<br />
* https://forum.lazarus.freepascal.org/index.php/topic,57680.0.html<br />
* https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/37509<br />
* https://gitlab.com/dbannon/laz_examples - this is just a temp home.<br />
* https://gitlab.com/dbannon/laz_examples/-/tree/main/Utility/ExScanner - a tool for manipulating Example Projects in and around the Lazarus Src Tree. Probably past its use by date. Also has a framework to 'exercise' the Lazarus Examples Window in a stand alone, easier to manage environment.<br />
* https://github.com/davidbannon/ExampleMetaData - a very simple editor to make and validate the Example Meta Data files.<br />
* https://gitlab.com/freepascal.org/lazarus/lazarus/-/blob/main/components/exampleswindow/uexampledata.pas the unit making decisions about your examples.<br />
<br />
<br />
<br />
[[Category:Example_programs]]<br />
[[Category:Development]]<br />
[[Category:Proposals]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Lazarus_Examples_Window&diff=158132Lazarus Examples Window2024-01-26T03:33:47Z<p>Dbannon: update now its about release code.</p>
<hr />
<div><br />
<br />
<br />
The new Example Window that exists in Lazarus 3.0 and beyond is documented below. It is based on this forum thread - https://forum.lazarus.freepascal.org/index.php/topic,57680.0.html and is by no means finalized.<br />
<br />
== For the end user ==<br />
<br />
A Lazarus user gets to the Example Window after clicking the button on the Project Wizard screen or from the Tools Menu, "Example Projects". In either case they see a window offering a list of known examples. The list can be filtered by key words or by category (double click between category checkboxes turns all off). In basic use, it shows you all the examples shipped with Lazarus. <br />
<br />
The Example Window will allow the user to open, build (and edit) and run any example but note some have specific package requirements. Lazarus Source examples can be refreshed if necessary. When opened, the examples from the Lazarus Source code are copied to a (user writable) area in the Lazarus Config directory ([[pcp|PCP]]) because, on some systems Lazarus Source is in read-only disk space. Further, this permits easy refresh of an example if the user makes changes.<br />
<br />
== For the Developer ==<br />
<br />
=== Examples in the Lazarus Source ===<br />
<br />
You can add an example anywhere you like in the Lazarus Source Tree, there are lots under ~/examples but also many associated with different Lazarus sub system. A few rules apply -<br />
<br />
* The Example Name must be unique within Lazarus (when lowercased).<br />
* The example should be self contained within its own directory. That directory (and any sub directories) will be copied to the work area so don't assume particular files are "just up one dir".<br />
* The example should have an Example Metadata File, with a file name that corresponds to the project name and an extension of .ex-meta. The content of this file is JSON and must have a name, a category and should have a (multiline) description and keywords. See below. <br />
<br />
If you add a new example (or remove one) to the Lazarus Source Tree, the file ~/examples/examples.txt needs to be refreshed. Its plain text, edit it directly or generate a new one with this command (on a *nix )-<br />
<br />
$> find . -name "*.ex-meta" > examples/examples.txt<br />
<br />
== Examples in Third Party Packages ==<br />
Again, you are free to place your package examples directory where you like but in order for the Lazarus Examples Window to find it, some things must be provided, note, different rules than above !<br />
<br />
* The package must be currently installed in Lazarus (check ? see if its listed in ([[pcp|PCP]])/staticpackages.inc)<br />
* Your example should be in a directory of its own. You should not put more than one example in the same directory.<br />
* In the case of Package Examples, the example directory is not moved to a work area so it can (but perhaps should not) be dependent on local, relative path files.<br />
* It must have a Metadata File, see below.<br />
* The Package file, that is the .lpk file must have entry such as <ExamplesDirectory Value="../demo"/>, typically just below the Author item. The value is the relative path from the .lpk file to a directory containing your example or examples. eg -<br />
[MyPackage]<br />
[demo]<br />
mypackage_demo1.ex-meta<br />
....<br />
[demo2]<br />
mypackage_demo2.ex-meta<br />
....<br />
[package]<br />
mypackage.lpk<br />
<br />
(Personally, I would put demo1 in its own directory too but the above would work !)<br />
<br />
'''''Note that, at present, Lazarus, when making a package, does NOT create the ExamplesDirectory entry, it will, but its a work in progress. If you want to test, manually adding that entry is the way to go right now.'''''<br />
<br />
== The Metadata File ==<br />
<br />
All examples that appear in the Example Window have a json metadata file with an extension ".ex-meta" in the top level directory of the example (not the package). Without a valid metadata file, the example will not be listed in the Example Window. An individual example project file would look like this -<br />
<br />
<syntaxhighlight lang=text>{ <br />
"Laz_Hello" : {<br />
"Category" : "Beginner",<br />
"Keywords" : ["Lazarus", "Hello World", "TButton", "TMemo"],<br />
"Description" : "This might be your first Lazarus project, its the traditional Hello World. Two buttons have been dropped on the form, renamed and their captions set. A memo was then added and each button was double clicked and what they are expected to do was set."<br />
}</syntaxhighlight><br />
<br />
In this case, the example project files would be in a subdirectory called laz_hello, same spelling but all lowercase consistent with Lazarus. <br />
<br />
The three fields are free form text, there is no dictionary so, you are free to introduce both new Categories and new Key Words. Its desirable that we do not have too many Categories so, perhaps consider posting a message on the forum before using a new one ? At present, the following Categories are in use -<br />
* Beginner<br />
* General<br />
* TAChart<br />
* DBase<br />
* LazReport<br />
* ThirdParty<br />
<br />
There is a rough and ready GUI app to make, edit and importantly, validate metadata files available at https://github.com/davidbannon/ExampleMetaData .<br />
<br />
== Can the IDE find the example ?==<br />
<br />
If the IDE does not find an example, some things to check for -<br />
<br />
* Does it have a metadata file ?<br />
* Is there a message about the metadata from console ?<br />
* If its a Lazarus Source example, is it mentioned in examples/examples.txt ?<br />
* if a Package Example, is it mentioned in ([[pcp|PCP]])/staticpackages.inc ? and in ([[pcp|PCP]])/packagefiles.xml ?<br />
<br />
==Notes==<br />
The user can view the project content and, if they choose, build the project. Later, if the user users the Example Window to again open that project, they will be asked if they want to refresh it, doing so will erase any false edits they may have made.<br />
<br />
Important to note -<br />
* Example Projects will not be displayed in the Examples Window unless they have a valid metadata file. An error is dropped to the console if a metadata file with bad json is encountered.<br />
* The original copy of the Example is not altered when the Example Window is used to open an Example. So, play away !<br />
* New examples can, potentially be added via the normal Gitlab pull request system. <br />
* Examples demonstrating aspects of third party packages are probably best added to that package rather than to Lazarus itself.<br />
<br />
== See also ==<br />
<br />
* https://forum.lazarus.freepascal.org/index.php/topic,57680.0.html<br />
* https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/37509<br />
* https://gitlab.com/dbannon/laz_examples - this is just a temp home.<br />
* https://gitlab.com/dbannon/laz_examples/-/tree/main/Utility/ExScanner - a tool for manipulating Example Projects in and around the Lazarus Src Tree. Probably past its use by date. Also has a framework to 'exercise' the Lazarus Examples Window in a stand alone, easier to manage environment.<br />
* https://github.com/davidbannon/ExampleMetaData - a very simple editor to make and validate the Example Meta Data files.<br />
* https://gitlab.com/freepascal.org/lazarus/lazarus/-/blob/main/components/exampleswindow/uexampledata.pas the unit making decisions about your examples.<br />
<br />
<br />
<br />
[[Category:Example_programs]]<br />
[[Category:Development]]<br />
[[Category:Proposals]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Lazarus_Examples_Window&diff=158131Lazarus Examples Window2024-01-26T03:30:06Z<p>Dbannon: update now its about release code.</p>
<hr />
<div><br />
<br />
<br />
The new Example Window that exists in Lazarus 3.0 and beyond is documented below. It is based on this forum thread - https://forum.lazarus.freepascal.org/index.php/topic,57680.0.html and is by no means finalized.<br />
<br />
== For the end user ==<br />
<br />
A Lazarus user gets to the Example Window after clicking the button on the Project Wizard screen or from the Tools Menu, "Example Projects". In either case they see a window offering a list of known examples. The list can be filtered by key words or by category (double click between category checkboxes turns all off). In basic use, it shows you all the examples shipped with Lazarus. <br />
<br />
The Example Window will allow the user to open, build (and edit) and run any example but note some have specific package requirements. Lazarus Source examples can be refreshed if necessary. When opened, the examples from the Lazarus Source code are copied to a (user writable) area in the Lazarus Config directory ([[pcp|PCP]]) because, on some systems Lazarus Source is in read-only disk space. Further, this permits easy refresh of an example if the user makes changes.<br />
<br />
== Examples in the Lazarus Source ==<br />
<br />
You can add an example anywhere you like in the Lazarus Source Tree, there are lots under ~/examples but also many associated with different Lazarus sub system. A few rules apply -<br />
<br />
* The Example Name must be unique within Lazarus (when lowercased).<br />
* The example should be self contained within its own directory. That directory (and any sub directories) will be copied to the work area so don't assume particular files are "just up one dir".<br />
* The example should have an Example Metadata File, with a file name that corresponds to the project name and an extension of .ex-meta. The content of this file is JSON and must have a name, a category and should have a (multiline) description and keywords. See below. <br />
<br />
If you add a new example (or remove one) to the Lazarus Source Tree, the file ~/examples/examples.txt needs to be refreshed. Its plain text, edit it directly or generate a new one with this command (on a *nix )-<br />
<br />
$> find . -name "*.ex-meta" > examples/examples.txt<br />
<br />
== Examples in Third Party Packages ==<br />
Again, you are free to place your package examples directory where you like but in order for the Lazarus Examples Window to find it, some things must be provided, note, different rules than above !<br />
<br />
* The package must be currently installed in Lazarus (check ? see if its listed in ([[pcp|PCP]])/staticpackages.inc)<br />
* Your example should be in a directory of its own. You should not put more than one example in the same directory.<br />
* In the case of Package Examples, the example directory is not moved to a work area so it can (but perhaps should not) be dependent on local, relative path files.<br />
* It must have a Metadata File, see below.<br />
* The Package file, that is the .lpk file must have entry such as <ExamplesDirectory Value="../demo"/>, typically just below the Author item. The value is the relative path from the .lpk file to a directory containing your example or examples. eg -<br />
[MyPackage]<br />
[demo]<br />
mypackage_demo1.ex-meta<br />
....<br />
[demo2]<br />
mypackage_demo2.ex-meta<br />
....<br />
[package]<br />
mypackage.lpk<br />
<br />
(Personally, I would put demo1 in its own directory too but the above would work !)<br />
<br />
'''''Note that, at present, Lazarus, when making a package, does NOT create the ExamplesDirectory entry, it will, but its a work in progress. If you want to test, manually adding that entry is the way to go right now.'''''<br />
<br />
== The Metadata File ==<br />
<br />
All examples that appear in the Example Window have a json metadata file with an extension ".ex-meta" in the top level directory of the example (not the package). Without a valid metadata file, the example will not be listed in the Example Window. An individual example project file would look like this -<br />
<br />
<syntaxhighlight lang=text>{ <br />
"Laz_Hello" : {<br />
"Category" : "Beginner",<br />
"Keywords" : ["Lazarus", "Hello World", "TButton", "TMemo"],<br />
"Description" : "This might be your first Lazarus project, its the traditional Hello World. Two buttons have been dropped on the form, renamed and their captions set. A memo was then added and each button was double clicked and what they are expected to do was set."<br />
}</syntaxhighlight><br />
<br />
In this case, the example project files would be in a subdirectory called laz_hello, same spelling but all lowercase consistent with Lazarus. <br />
<br />
The three fields are free form text, there is no dictionary so, you are free to introduce both new Categories and new Key Words. Its desirable that we do not have too many Categories so, perhaps consider posting a message on the forum before using a new one ? At present, the following Categories are in use -<br />
* Beginner<br />
* General<br />
* TAChart<br />
* DBase<br />
* LazReport<br />
* ThirdParty<br />
<br />
There is a rough and ready GUI app to make, edit and importantly, validate metadata files available at https://github.com/davidbannon/ExampleMetaData .<br />
<br />
== Can the IDE find the example ?==<br />
<br />
If the IDE does not find an example, some things to check for -<br />
<br />
* Does it have a metadata file ?<br />
* Is there a message about the metadata from console ?<br />
* If its a Lazarus Source example, is it mentioned in examples/examples.txt ?<br />
* if a Package Example, is it mentioned in ([[pcp|PCP]])/staticpackages.inc ? and in ([[pcp|PCP]])/packagefiles.xml ?<br />
<br />
==Notes==<br />
The user can view the project content and, if they choose, build the project. Later, if the user users the Example Window to again open that project, they will be asked if they want to refresh it, doing so will erase any false edits they may have made.<br />
<br />
Important to note -<br />
* Example Projects will not be displayed in the Examples Window unless they have a valid metadata file. An error is dropped to the console if a metadata file with bad json is encountered.<br />
* The original copy of the Example is not altered when the Example Window is used to open an Example. So, play away !<br />
* New examples can, potentially be added via the normal Gitlab pull request system. <br />
* Examples demonstrating aspects of third party packages are probably best added to that package rather than to Lazarus itself.<br />
<br />
== See also ==<br />
<br />
* https://forum.lazarus.freepascal.org/index.php/topic,57680.0.html<br />
* https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/37509<br />
* https://gitlab.com/dbannon/laz_examples - this is just a temp home.<br />
* https://gitlab.com/dbannon/laz_examples/-/tree/main/Utility/ExScanner - a tool for manipulating Example Projects in and around the Lazarus Src Tree. Probably past its use by date. Also has a framework to 'exercise' the Lazarus Examples Window in a stand alone, easier to manage environment.<br />
* https://github.com/davidbannon/ExampleMetaData - a very simple editor to make and validate the Example Meta Data files.<br />
* https://gitlab.com/freepascal.org/lazarus/lazarus/-/blob/main/components/exampleswindow/uexampledata.pas the unit making decisions about your examples.<br />
<br />
<br />
<br />
[[Category:Example_programs]]<br />
[[Category:Development]]<br />
[[Category:Proposals]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Lazarus_on_Raspberry_Pi&diff=157996Lazarus on Raspberry Pi2023-12-31T10:39:15Z<p>Dbannon: note about Correcting swap file size</p>
<hr />
<div>{{Lazarus_on_Raspberry_Pi}}<br />
<br />
[[File:Lazarus on Raspberry Pi Raspian Wheezy version 2012-10-28.png|thumb|200px|right|alt=Lazarus on Raspbian Wheezy.|Lazarus on Raspbian Wheezy]]<br />
<br />
{{Platform only|Raspberry Pi}}<br />
<br />
The '''Raspberry Pi''' is a credit-card-sized single-board computer. It has been developed in the UK by the Raspberry Pi Foundation with the intention of stimulating the teaching of basic computer science in schools. Raspberry Pis are also used for multiple other purposes that are as different as media servers, robotics and control engineering.<br />
<br />
The Raspberry Pi Foundation recommends [[Raspbian | the Pasperry Pi OS]] as standard operating system. Alternative systems running on RPI include RISC OS and various [[Portal:Linux|Linux]] distributions, as well as [[Portal:Android|Android]] and [[Portal:FreeBSD|FreeBSD]].<br />
<br />
Lazarus runs natively under the Raspbian operating system.<br />
<br />
==Installing and compiling Lazarus==<br />
<br />
=== Introduction ===<br />
Installing FPC and Lazarus on a Raspberry Pi is very little different from other Linux boxes, so please refer to [[Installing_Lazarus_on_Linux]]. <br />
How ever, there are some RasPi particularities worth noting.<br />
* RasPi do not always have enough memory to build Lazarus, this affects the routine rebuild of the IDE that happens when installing packages for example. The solution is to increase swap size (below).<br />
* The Lazarus SourceForge repository does not include a pre-made RasPi package. <br />
* If you are using the Raspberry Pi OS, its based on Debian and Debian has quite a long release cycle. So, at any one time, the repo provided Lazarus is likely to be out of date and, occasionally, so will the FPC be.<br />
* Notwithstanding the above point, at present, late 2023, the Debian Bookworm based Raspberry Pi OS has the appropriate FPC and a Lazarus that suits most people. This will change !<br />
<br />
=== Correcting swap file size ===<br />
(Info from forum user "Thaddy", updated.)<br />
If you have RPi with memory size less then 4Gb, and you want to build Lazarus from source or use FPCUPdeluxe or just rebuild the IDE after installing a package you need to allocate at least 1G if not 2G of swap.<br />
This appears to be particularly so with Raspberry Pi OS based on Bookworm rather than Bullseye. <br />
<br />
To increase swap you should firstly ensure you have a couple of gig free and -<br />
<br />
* sudo dphys-swapfile swapoff<br />
* sudo nano /etc/dphys-swapfile<br />
* in the file, find CONF_SWAPSIZE and change the value to 2048 (or 1024) and save it.<br />
* sudo dphys-swapfile setup<br />
* sudo dphys-swapfile swapon<br />
<br />
You should now see the file, /var/swap, increase to 2G (or 1G). On some other operating systems, eg a "pure" debian, you may need to install the dphys-swapfile package. <br />
<br />
If you don't have enough memory (real memory and swap) then, your compile processes (particularly assembling) will stop and the whole machine freezes. You have been warned !<br />
<br />
While you are fiddling with the swap file, consider, if possible, moving the swap file from /var/swap to somewhere off the SDCard, unfortunately, SDCards are not good at the repetitive writes that happen in a swap file. Not critical but a good idea.<br />
<br />
If you have problems making the swap file, see this https://forums.raspberrypi.com/viewtopic.php?p=1881546<br />
<br />
===Simple installation ===<br />
<br />
Most distributions have a graphic installer of some sort, look for entries like "Add / Remove Software" in your menus. Alternatively, all systems will have a command line interface. As hinted above, its likely that your distribution will have a current FPC, if so, best to use it. More interesting is Lazarus, as it changes more frequently, consider your needs. The Raspberry Pi OS, late 2023, has Lazarus 2.2.6. While version 3.0 of Lazarus is released now, it will take awhile to make it into distro's repos. Some users will, for the time being, be quite happy with Lazarus 2.2.6. Later on, you can remove the distro supplied Lazarus 2.2.6 and build 3.0 from source yourself, its easy.<br />
<br />
A advantage of using the distro supplied FPC and Lazarus is that they test them, make sure everything needed is there and its very easy. <br />
<br />
See [https://wiki.freepascal.org/Installing_Lazarus_on_Linux#The_Package_Manager_Model the Package Manager Model].<br />
<br />
=== Not Quite as Simple Install ===<br />
<br />
There are two other functional alternatives for installing Lazarus, building from source and using [[fpcupdeluxe]], an application that you run on your computer to manage your FPC / Lazarus install. Its very suited to people who just want to get a working install quickly, fpcupdeluxe will do the thinking for them. Fpcupdelux will install (and manage) both FPC and Lazarus and you should use them together.<br />
<br />
Installing a non distro FPC is a touch more complicated still. Its [https://wiki.freepascal.org/Installing_the_Free_Pascal_Compiler#Compiling_the_FPC_source.2C_using_preinstalled_FPC possible] to build a new FPC using an older, existing, FPC, might be a challenge for a less powerful Pi. An better option is to [https://wiki.freepascal.org/Installing_the_Free_Pascal_Compiler#Downloading_FPC_tar_balls download and install] a tarball. <br />
<br />
Its quite reasonable to use the distro provided FPC and build your own Lazarus. In fact, many users have several lazarus versions installed this way. <br />
<br />
Building Lazarus [https://wiki.freepascal.org/Installing_Lazarus_on_Linux#Build_Lazarus_from_Source from source] on a Raspberry Pi is no different from other Linuxes, except, perhaps, for the [[Lazarus_on_Raspberry_Pi#Correcting_swap_file_size | memory issue]].<br />
<br />
<gallery><br />
File:Lazarus 0 9 30 4 on RPi 2.png|Lazarus "out of the box" on Raspberry Pi 2<br />
</gallery><br />
<br />
===Cross compiling for the Raspberry Pi from Windows===<br />
1. Using fpcup<br />
<br />
One way is to use fpcup to set up a cross compiler; follow these instructions:<br />
[[fpcup#Linux_ARM_cross_compiler]]<br />
<br />
2. Using scripts<br />
<br />
Alternatively, for a more manual approach using batch files, you can follow these steps.<br />
<br />
2.1 Prerequisites<br />
FPC 2.7.1 or higher installed with source code<br />
Install the Windows version from the Linaro binutils for linux gnueabihf into %FPCPATH%/bin/win32-armhf-linux [https://launchpad.net/linaro-toolchain-binaries/trunk/2013.10/+download/gcc-linaro-arm-linux-gnueabihf-4.8-2013.10_win32.zip]<br />
<br />
2.2 Example Windows Batch file (adapt paths as needed)<br />
<br />
<syntaxhighlight lang="bat"><br />
set PATH=C:\pp\bin\i386-win32;%PATH%;<br />
set FPCMAKEPATH=C:/pp<br />
set FPCPATH=C:/pp<br />
set OUTPATH=C:/pp271<br />
%FPCMAKEPATH%/bin/i386-win32/make distclean OS_TARGET=linux CPU_TARGET=arm CROSSBINDIR=%FPCPATH%/bin/win32-armhf-linux CROSSOPT="-CpARMV6 -CfVFPV2 -OoFASTMATH" FPC=%FPCPATH%/bin/i386-win32/ppc386.exe<br />
<br />
%FPCMAKEPATH%/bin/i386-win32/make all OS_TARGET=linux CPU_TARGET=arm CROSSBINDIR=%FPCPATH%/bin/win32-armhf-linux CROSSOPT="-CpARMV6 -CfVFPV2 -OoFASTMATH" FPC=%FPCPATH%/bin/i386-win32/ppc386.exe<br />
if errorlevel 1 goto quit<br />
%FPCMAKEPATH%/bin/i386-win32/make crossinstall CROSSBINDIR=%FPCPATH%/bin/win32-armhf-linux CROSSOPT="-CpARMV6 -CfVFPV2 -OoFASTMATH" OS_TARGET=linux CPU_TARGET=arm FPC=%FPCPATH%/bin/i386-win32/ppc386.exe INSTALL_BASEDIR=%OUTPATH%<br />
<br />
:quit<br />
pause<br />
</syntaxhighlight><br />
<br />
With the resulting ppcrossarm.exe and ARM RTL you will be able to build a cross Lazarus version as usual and compile FPC projects for the Raspberry Pi and other armhf devices.<br />
<br />
Remember that not all - especially Windows - libraries are available for Linux arm.<br />
<br />
===Cross compiling for the Raspberry Pi from Linux===<br />
<br />
Please see this page https://wiki.freepascal.org/Cross_Compile_to_RasPi_from_Linux<br />
<br />
==Accessing external hardware==<br />
[[File:rpi pinout all 50.png|thumb|300px|right|alt=Raspberry Pi pinout|Raspberry Pi pinout of external connectors]]<br />
<br />
One of the goals in the development of Raspberry Pi was to facilitate effortless access to external devices like sensors and actuators. There are many ways to access the I/O facilities from Lazarus and Free Pascal:<br />
# [[Lazarus on Raspberry Pi# Native hardware access|Direct access]] using the [[BaseUnix]] unit<br />
# Access through [[Lazarus on Raspberry Pi# Hardware access via encapsulated shell calls|encapsulated shell calls]]<br />
# Access through the [[Lazarus on Raspberry Pi# wiringPi procedures and functions|wiringPi library]].<br />
# Access through Unit [[Lazarus on Raspberry Pi# rpi_hal-Hardware Abstraction Library (GPIO, I2C und SPI functions and procedures)|rpi_hal]].<br />
# Access through Unit [[Lazarus on Raspberry Pi# PiGpio Low-level native pascal unit (GPIO control instead of wiringPi c library)|PiGpio]].<br />
# Access through the [https://github.com/SAmeis/pascalio PascalIO] library.<br />
# Access through the [http://asphyre.net/products/pxl PXL] library.<br />
<br />
=== Native hardware access===<br />
[[File:testprogram for GPIO on RPI annotated.png|Simple test program for accessing the GPIO port on Raspberry Pi|thumb|300px|right]]<br />
[[File:rpi testcircuit 1d.png|Test circuit for GPIO access with the described program|thumb|300px|right]]<br />
[[File:rpi testcircuit 1p annotaded.png|Simple demo implementation of the circuit from above on a breadboard|thumb|300px|right]]<br />
This method provides access to external hardware that doesn't require additional libraries. The only requirement is the BaseUnix library that is part of Free Pascal's RTL.<br />
<br />
==== Switching a device via the GPIO port ====<br />
The following example lists a simple program that controls the GPIO pin 17 as output to switch an LED, transistor or relays. This program contains a [[doc:lcl/stdctrls/ttogglebox.html|ToggleBox]] with name ''GPIO17ToggleBox'' and for logging return codes a [[doc:lcl/stdctrls/tmemo.html|TMemo]] called ''LogMemo''.<br />
<br />
For the example, the anode of a LED has been connected with Pin 11 on the Pi's connector (corresponding to GPIO pin 17 of the BCM2835 SOC) and the LED's cathode was wired via a 68 Ohm resistor to pin 6 of the connector (GND) as previously described by Upton and Halfacree. Subsequently, the LED may be switched on and off with the application's toggle box.<br />
<br />
The code may at first require to be run as root, i.e. either from a root account (not recommended) or via '''su'''.<br />
A better option is to add the user to the gpio group, the i2c group and the spi group.<br />
<br />
<syntaxhighlight lang="bash"><br />
sudo adduser pi gpio<br />
sudo adduser pi i2c<br />
sudo adduser pi spi<br />
</syntaxhighlight><br />
<br />
''Controlling unit:''<br />
<syntaxhighlight lang="pascal"><br />
unit Unit1;<br />
<br />
{Demo application for GPIO on Raspberry Pi}<br />
{Inspired by the Python input/output demo application by Gareth Halfacree}<br />
{written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5}<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
Unix, BaseUnix;<br />
<br />
type<br />
<br />
{ TForm1 }<br />
<br />
TForm1 = class(TForm)<br />
LogMemo: TMemo;<br />
GPIO17ToggleBox: TToggleBox;<br />
procedure FormActivate(Sender: TObject);<br />
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
procedure GPIO17ToggleBoxChange(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
const<br />
PIN_17: PChar = '17';<br />
PIN_ON: PChar = '1';<br />
PIN_OFF: PChar = '0';<br />
OUT_DIRECTION: PChar = 'out';<br />
<br />
var<br />
Form1: TForm1;<br />
gReturnCode: longint; {stores the result of the IO operation}<br />
<br />
implementation<br />
<br />
{$R *.lfm}<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.FormActivate(Sender: TObject);<br />
var<br />
fileDesc: integer;<br />
begin<br />
{ Prepare SoC pin 17 (pin 11 on GPIO port) for access: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/export', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, PIN_17[0], 2);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
{ Set SoC pin 17 as output: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/gpio17/direction', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, OUT_DIRECTION[0], 3);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end;<br />
<br />
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
var<br />
fileDesc: integer;<br />
begin<br />
{ Free SoC pin 17: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/unexport', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, PIN_17[0], 2);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end;<br />
<br />
procedure TForm1.GPIO17ToggleBoxChange(Sender: TObject);<br />
var<br />
fileDesc: integer;<br />
begin<br />
if GPIO17ToggleBox.Checked then<br />
begin<br />
{ Swith SoC pin 17 on: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/gpio17/value', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, PIN_ON[0], 1);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end<br />
else<br />
begin<br />
{ Switch SoC pin 17 off: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/gpio17/value', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, PIN_OFF[0], 1);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
''Main program:''<br />
<syntaxhighlight lang="pascal"><br />
program io_test;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
uses<br />
{$IFDEF UNIX}{$IFDEF UseCThreads}<br />
cthreads,<br />
{$ENDIF}{$ENDIF}<br />
Interfaces, // this includes the LCL widgetset<br />
Forms, Unit1<br />
{ you can add units after this };<br />
<br />
{$R *.res}<br />
<br />
begin<br />
Application.Initialize;<br />
Application.CreateForm(TForm1, Form1);<br />
Application.Run;<br />
end.<br />
</syntaxhighlight><br />
<br />
==== Reading the status of a pin ====<br />
[[File:2013-04-28-193243 1280x1024 scrot.png|Demo program for reading the status of a GPIO pin|thumb|300px|right]]<br />
[[File:rpi testcircuit 2d.png|Test circuit for GPIO access with the described program|thumb|300px|right]]<br />
[[File:rpi testcircuit 2p annotaded.png|Possible implementation of this test circuit|thumb|300px|right]]<br />
<br />
Of course it is also possible to read the status of e.g. a switch that is connected to the GPIO port.<br />
<br />
The following simple example is very similar to the previous one. It controls the GPIO pin 18 as input for a binary device like a switch, transistor or relais. This program contains a [[doc:lcl/stdctrls/tcheckbox.html|CheckBox]] with name ''GPIO18CheckBox'' and for logging return codes a [[doc:lcl/stdctrls/tmemo.html|TMemo]] called ''LogMemo''.<br />
<br />
For the example, one pole of a push-button has been connected to Pin 12 on the Pi's connector (corresponding to GPIO pin 18 of the BCM2835 SOC) and via a 10k Ohm pull-up resistor with pin 1 (+3.3V, see wiring diagram). The other pole has been wired to pin 6 of the connector (GND). The program senses the status of the button and correspondingly switches the checkbox on or off, respectively.<br />
<br />
Note that the potential of pin 18 is high if the button is released (by virtue of the connection to pin 1 via the pull-up resistor) and low if it is pressed (since in this situation pin 18 is connected to GND via the switch). Therefore, the GPIO pin signals 0 if the button is pressed and 1 if it is released.<br />
<br />
This program has again to be executed as root.<br />
<br />
''Controlling unit:''<br />
<syntaxhighlight lang="pascal"><br />
unit Unit1;<br />
<br />
{Demo application for GPIO on Raspberry Pi}<br />
{Inspired by the Python input/output demo application by Gareth Halfacree}<br />
{written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5}<br />
<br />
{This application reads the status of a push-button}<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
ButtonPanel, Unix, BaseUnix;<br />
<br />
type<br />
<br />
{ TForm1 }<br />
<br />
TForm1 = class(TForm)<br />
ApplicationProperties1: TApplicationProperties;<br />
GPIO18CheckBox: TCheckBox;<br />
LogMemo: TMemo;<br />
procedure ApplicationProperties1Idle(Sender: TObject; var Done: Boolean);<br />
procedure FormActivate(Sender: TObject);<br />
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
const<br />
PIN_18: PChar = '18';<br />
IN_DIRECTION: PChar = 'in';<br />
<br />
var<br />
Form1: TForm1;<br />
gReturnCode: longint; {stores the result of the IO operation}<br />
<br />
implementation<br />
<br />
{$R *.lfm}<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.FormActivate(Sender: TObject);<br />
var<br />
fileDesc: integer;<br />
begin<br />
{ Prepare SoC pin 18 (pin 12 on GPIO port) for access: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/export', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, PIN_18[0], 2);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
{ Set SoC pin 18 as input: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/gpio18/direction', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, IN_DIRECTION[0], 2);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end;<br />
<br />
procedure TForm1.ApplicationProperties1Idle(Sender: TObject; var Done: Boolean);<br />
var<br />
fileDesc: integer;<br />
buttonStatus: string[1] = '1';<br />
begin<br />
try<br />
{ Open SoC pin 18 (pin 12 on GPIO port) in read-only mode: }<br />
fileDesc := fpopen('/sys/class/gpio/gpio18/value', O_RdOnly);<br />
if fileDesc > 0 then<br />
begin<br />
{ Read status of this pin (0: button pressed, 1: button released): }<br />
gReturnCode := fpread(fileDesc, buttonStatus[1], 1);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode) + ': ' + buttonStatus);<br />
LogMemo.SelStart := Length(LogMemo.Lines.Text) - 1;<br />
if buttonStatus = '0' then<br />
GPIO18CheckBox.Checked := true<br />
else<br />
GPIO18CheckBox.Checked := false;<br />
end;<br />
finally<br />
{ Close SoC pin 18 (pin 12 on GPIO port) }<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
LogMemo.SelStart := Length(LogMemo.Lines.Text) - 1;<br />
end;<br />
end;<br />
<br />
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
var<br />
fileDesc: integer;<br />
begin<br />
{ Free SoC pin 18: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/unexport', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, PIN_18[0], 2);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
The main program is identical to that of the example from above.<br />
<br />
=== Hardware access via encapsulated shell calls===<br />
Another way to access the hardware is by encapsulating terminal commands. This is achieved by using the [[Executing External Programs#Unix fpsystem, fpexecve and shell|fpsystem]] function. This method gives access to functions that are not supported by the BaseUnix unit. The following code implements a program that has the same functionality as the program resulting from the first listing above.<br />
<br />
''Controlling unit:''<br />
<syntaxhighlight lang="pascal"><br />
unit Unit1;<br />
<br />
{Demo application for GPIO on Raspberry Pi}<br />
{Inspired by the Python input/output demo application by Gareth Halfacree}<br />
{written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5}<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, Unix;<br />
<br />
type<br />
<br />
{ TForm1 }<br />
<br />
TForm1 = class(TForm)<br />
LogMemo: TMemo;<br />
GPIO17ToggleBox: TToggleBox;<br />
procedure FormActivate(Sender: TObject);<br />
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
procedure GPIO17ToggleBoxChange(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
var<br />
Form1: TForm1;<br />
gReturnCode: longint; {stores the result of the IO operation}<br />
<br />
implementation<br />
<br />
{$R *.lfm}<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.FormActivate(Sender: TObject);<br />
begin<br />
{ Prepare SoC pin 17 (pin 11 on GPIO port) for access: }<br />
gReturnCode := fpsystem('echo "17" > /sys/class/gpio/export');<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
{ Set SoC pin 17 as output: }<br />
gReturnCode := fpsystem('echo "out" > /sys/class/gpio/gpio17/direction');<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
<br />
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
begin<br />
{ Free SoC pin 17: }<br />
gReturnCode := fpsystem('echo "17" > /sys/class/gpio/unexport');<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
<br />
procedure TForm1.GPIO17ToggleBoxChange(Sender: TObject);<br />
begin<br />
if GPIO17ToggleBox.Checked then<br />
begin<br />
{ Swith SoC pin 17 on: }<br />
gReturnCode := fpsystem('echo "1" > /sys/class/gpio/gpio17/value');<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end<br />
else<br />
begin<br />
{ Switch SoC pin 17 off: }<br />
gReturnCode := fpsystem('echo "0" > /sys/class/gpio/gpio17/value');<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
The main program is identical to that of the example above. This program has to be executed with root privileges, too.<br />
<br />
=== wiringPi procedures and functions===<br />
<br />
Alex Schaller's wrapper unit for Gordon Henderson's Arduino compatible wiringPi library provides a numbering scheme that resembles that of Arduino boards.<br />
<br />
'''Function wiringPiSetup:longint:''' Initializes wiringPi system using the wiringPi pin numbering scheme.<br />
<br />
'''Procedure wiringPiGpioMode(mode:longint):''' Initializes wiringPi system with the Broadcom GPIO pin numbering scheme.<br />
<br />
'''Procedure pullUpDnControl(pin:longint;''' pud:longint): controls the internal pull-up/down resistors on a GPIO pin.<br />
<br />
'''Procedure pinMode(pin:longint; mode:longint):''' sets the mode of a pin to either INPUT, OUTPUT, or PWM_OUTPUT.<br />
<br />
'''Procedure digitalWrite(pin:longint; value:longint):''' sets an output bit.<br />
<br />
'''Procedure pwmWrite(pin:longint; value:longint):''' sets an output PWM value between 0 and 1024.<br />
<br />
'''Function digitalRead(pin:longint):longint:''' reads the value of a given Pin, returning 1 or 0.<br />
<br />
'''Procedure delay(howLong:dword):''' waits for at least howLong milliseconds.<br />
<br />
'''Procedure delayMicroseconds(howLong:dword):''' waits for at least howLong microseconds.<br />
<br />
'''Function millis:dword:''' returns the number of milliseconds since the program called one of the wiringPiSetup functions.<br />
<br />
=== rpi_hal-Hardware Abstraction Library (GPIO, I2C and SPI functions and procedures)===<br />
<br />
This Unit with around 1700 Lines of Code provided by Stefan Fischer, delivers procedures and functions to access the rpi HW I2C, SPI and GPIO:<br />
<br />
Just an excerpt of the available functions and procedures:<br />
<br />
'''procedure gpio_set_pin (pin:longword;highlevel:boolean);''' { Set RPi GPIO pin to high or low level; Speed @ 700MHz -> 0.65MHz }<br />
<br />
'''function gpio_get_PIN (pin:longword):boolean;''' { Get RPi GPIO pin Level is true when Pin level is '1'; false when '0'; Speed @ 700MHz -> 1.17MHz } <br />
<br />
'''procedure gpio_set_input (pin:longword);''' { Set RPi GPIO pin to input direction }<br />
<br />
'''procedure gpio_set_output(pin:longword);''' { Set RPi GPIO pin to output direction }<br />
<br />
'''procedure gpio_set_alt (pin,altfunc:longword);''' { Set RPi GPIO pin to alternate function nr. 0..5 }<br />
<br />
'''procedure gpio_set_gppud (mask:longword);''' { set RPi GPIO Pull-up/down Register (GPPUD) with mask }<br />
<br />
...<br />
<br />
'''function rpi_snr :string;''' { delivers SNR: 0000000012345678 }<br />
<br />
'''function rpi_hw :string;''' { delivers Processor Type: BCM2708 }<br />
<br />
'''function rpi_proc:string;''' { ARMv6-compatible processor rev 7 (v6l) }<br />
<br />
...<br />
<br />
'''function i2c_bus_write(baseadr,reg:word; var data:databuf_t; lgt:byte; testnr:integer) : integer;''' <br />
<br />
'''function i2c_bus_read (baseadr,reg:word; var data:databuf_t; lgt:byte; testnr:integer) : integer;'''<br />
<br />
'''function i2c_string_read(baseadr,reg:word; var data:databuf_t; lgt:byte; testnr:integer) : string;''' <br />
<br />
'''function i2c_string_write(baseadr,reg:word; s:string; testnr:integer) : integer;'''<br />
<br />
...<br />
<br />
'''procedure SPI_Write(devnum:byte; reg,data:word);'''<br />
<br />
'''function SPI_Read(devnum:byte; reg:word) : byte;'''<br />
<br />
'''procedure SPI_BurstRead2Buffer (devnum,start_reg:byte; xferlen:longword);'''<br />
<br />
'''procedure SPI_BurstWriteBuffer (devnum,start_reg:byte; xferlen:longword);''' { Write 'len' Bytes from Buffer SPI Dev startig at address 'reg' }<br />
<br />
... <br />
<br />
<br />
''Test Program (testrpi.pas):''<br />
<syntaxhighlight lang="pascal"><br />
//Simple Test program, which is using rpi_hal;<br />
<br />
program testrpi;<br />
uses rpi_hal;<br />
begin<br />
writeln('Show CPU-Info, RPI-HW-Info and Registers:');<br />
rpi_show_all_info;<br />
writeln('Let Status LED Blink. Using GPIO functions:');<br />
GPIO_PIN_TOGGLE_TEST;<br />
writeln('Test SPI Read function. (piggy back board with installed RFM22B Module is required!)');<br />
Test_SPI;<br />
end.<br />
<br />
</syntaxhighlight><br />
<br />
=== PiGpio Low-level native pascal unit (GPIO control instead of wiringPi c library)===<br />
<br />
''This Unit (pigpio.pas[https://docs.google.com/file/d/0B-b-5ooIWPvGN1BIcEJzRDgyeUE/edit?usp=sharing]) with 270 Lines of Code provided by Gabor Szollosi, works very fast (for ex. GPIO pin output switching frequency 8 MHz) :''<br />
<syntaxhighlight lang="pascal"><br />
<br />
unit PiGpio;<br />
{<br />
BCM2835 GPIO Registry Driver, also can use to manipulate cpu other registry areas<br />
<br />
This code is tested only Broadcom bcm2835 cpu, different arm cpus may need different<br />
gpio driver implementation<br />
<br />
2013 Gabor Szollosi<br />
}<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils;<br />
<br />
const<br />
REG_GPIO = $3F000;// bcm2835 gpio register 0x2000 0000 (RPi1), 0x3F00 0000 (RPi2)<br />
// new fpMap uses page offset, one page is 4096 bytes = 0x1000 so simply calculate 0x2000 0000 / 0x1000 = 0x2000 0<br />
PAGE_SIZE = 4096;<br />
BLOCK_SIZE = 4096;<br />
// The BCM2835 has 54 GPIO pins.<br />
// BCM2835 data sheet, Page 90 onwards.<br />
// There are 6 control registers, each control the functions of a block<br />
// of 10 pins.<br />
<br />
CLOCK_BASE = (REG_GPIO + $101);<br />
GPIO_BASE = (REG_GPIO + $200);<br />
GPIO_PWM = (REG_GPIO + $20C);<br />
<br />
INPUT = 0;<br />
OUTPUT = 1;<br />
PWM_OUTPUT = 2;<br />
LOW = False;<br />
HIGH = True;<br />
PUD_OFF = 0;<br />
PUD_DOWN = 1;<br />
PUD_UP = 2;<br />
<br />
// PWM<br />
<br />
PWM_CONTROL = 0;<br />
PWM_STATUS = 4;<br />
PWM0_RANGE = 16;<br />
PWM0_DATA = 20;<br />
PWM1_RANGE = 32;<br />
PWM1_DATA = 36;<br />
<br />
PWMCLK_CNTL = 160;<br />
PWMCLK_DIV = 164;<br />
<br />
PWM1_MS_MODE = $8000; // Run in MS mode<br />
PWM1_USEFIFO = $2000; // Data from FIFO<br />
PWM1_REVPOLAR = $1000; // Reverse polarity<br />
PWM1_OFFSTATE = $0800; // Ouput Off state<br />
PWM1_REPEATFF = $0400; // Repeat last value if FIFO empty<br />
PWM1_SERIAL = $0200; // Run in serial mode<br />
PWM1_ENABLE = $0100; // Channel Enable<br />
<br />
PWM0_MS_MODE = $0080; // Run in MS mode<br />
PWM0_USEFIFO = $0020; // Data from FIFO<br />
PWM0_REVPOLAR = $0010; // Reverse polarity<br />
PWM0_OFFSTATE = $0008; // Ouput Off state<br />
PWM0_REPEATFF = $0004; // Repeat last value if FIFO empty<br />
PWM0_SERIAL = $0002; // Run in serial mode<br />
PWM0_ENABLE = $0001; // Channel Enable<br />
<br />
<br />
type<br />
<br />
{ TIoPort }<br />
<br />
TIoPort = class // IO bank object<br />
private //<br />
<br />
//function get_pinDirection(aPin: TGpIoPin): TGpioPinConf;<br />
<br />
public<br />
FGpio: ^LongWord;<br />
FClk: ^LongWord;<br />
FPwm: ^LongWord;<br />
procedure SetPinMode(gpin, mode: byte);<br />
function GetBit(gpin : byte):boolean;inline; // gets pin bit}<br />
procedure ClearBit(gpin : byte);inline;// write pin to 0<br />
procedure SetBit(gpin : byte);inline;// write pin to 1<br />
procedure SetPullMode(gpin, mode: byte);<br />
procedure PwmWrite(gpin : byte; value : LongWord);inline;// write pin to pwm value<br />
end;<br />
<br />
{ TIoDriver }<br />
<br />
TIoDriver = class<br />
private<br />
<br />
public<br />
destructor Destroy;override;<br />
function MapIo:boolean;// creates io memory mapping<br />
procedure UnmapIoRegisrty(FMap: TIoPort);// close io memory mapping<br />
function CreatePort(PortGpio, PortClk, PortPwm: LongWord):TIoPort; // create new IO port<br />
end;<br />
<br />
var<br />
<br />
fd: integer;// /dev/mem file handle<br />
procedure delayNanoseconds (howLong : LongWord);<br />
<br />
<br />
implementation<br />
<br />
uses<br />
baseUnix, Unix;<br />
<br />
procedure delayNanoseconds (howLong : LongWord);<br />
var<br />
sleeper, dummy : timespec;<br />
begin<br />
sleeper.tv_sec := 0 ;<br />
sleeper.tv_nsec := howLong ;<br />
fpnanosleep (@sleeper,@dummy) ;<br />
end;<br />
{ TIoDriver }<br />
//*******************************************************************************<br />
destructor TIoDriver.Destroy;<br />
begin<br />
inherited Destroy;<br />
end;<br />
//*******************************************************************************<br />
function TIoDriver.MapIo: boolean;<br />
begin<br />
Result := True;<br />
fd := fpopen('/dev/mem', O_RdWr or O_Sync); // Open the master /dev/memory device<br />
if fd < 0 then<br />
begin<br />
Result := False; // unsuccessful memory mapping<br />
end;<br />
//<br />
end;<br />
//*******************************************************************************<br />
procedure TIoDriver.UnmapIoRegisrty(FMap:TIoPort);<br />
begin<br />
if FMap.FGpio <> nil then<br />
begin<br />
fpMUnmap(FMap.FGpio,PAGE_SIZE);<br />
FMap.FGpio := Nil;<br />
end;<br />
if FMap.FClk <> nil then<br />
begin<br />
fpMUnmap(FMap.FClk,PAGE_SIZE);<br />
FMap.FClk := Nil;<br />
end;<br />
if FMap.FPwm <> nil then<br />
begin<br />
fpMUnmap(FMap.FPwm ,PAGE_SIZE);<br />
FMap.FPwm := Nil;<br />
end;<br />
end;<br />
//*******************************************************************************<br />
function TIoDriver.CreatePort(PortGpio, PortClk, PortPwm: LongWord): TIoPort;<br />
begin<br />
Result := TIoPort.Create;// new io port, pascal calls new fpMap, where offst is page sized 4096 bytes!!!<br />
Result.FGpio := FpMmap(Nil, PAGE_SIZE, PROT_READ or PROT_WRITE, MAP_SHARED, fd, PortGpio); // port config gpio memory<br />
Result.FClk:= FpMmap(Nil, PAGE_SIZE, PROT_READ or PROT_WRITE, MAP_SHARED, fd, PortClk);; // port clk<br />
Result.FPwm:= FpMmap(Nil, PAGE_SIZE, PROT_READ or PROT_WRITE, MAP_SHARED, fd, PortPwm);; // port pwm<br />
end;<br />
//*******************************************************************************<br />
procedure TIoPort.SetPinMode(gpin, mode: byte);<br />
var<br />
fSel, shift, alt : byte;<br />
gpiof, clkf, pwmf : ^LongWord;<br />
begin<br />
fSel := (gpin div 10)*4 ; //Select Gpfsel 0 to 5 register<br />
shift := (gpin mod 10)*3 ; //0-9 pin shift<br />
gpiof := Pointer(LongWord(Self.FGpio)+fSel);<br />
if (mode = INPUT) then<br />
gpiof^ := gpiof^ and ($FFFFFFFF - (7 shl shift)) //7 shl shift komplemens - Sets bits to zero = input<br />
else if (mode = OUTPUT) then<br />
begin<br />
gpiof^ := gpiof^ and ($FFFFFFFF - (7 shl shift)) or (1 shl shift);<br />
end<br />
else if (mode = PWM_OUTPUT) then<br />
begin<br />
Case gpin of<br />
12,13,40,41,45 : alt:= 4 ;<br />
18,19 : alt:= 2 ;<br />
else alt:= 0 ;<br />
end;<br />
If alt > 0 then<br />
begin<br />
gpiof^ := gpiof^ and ($FFFFFFFF - (7 shl shift)) or (alt shl shift);<br />
clkf := Pointer(LongWord(Self.FClk)+PWMCLK_CNTL);<br />
clkf^ := $5A000011 or (1 shl 5) ; //stop clock<br />
delayNanoseconds(200);<br />
clkf := Pointer(LongWord(Self.FClk)+PWMCLK_DIV);<br />
clkf^ := $5A000000 or (32 shl 12) ; // set pwm clock div to 32 (19.2/3 = 600KHz)<br />
clkf := Pointer(LongWord(Self.FClk)+PWMCLK_CNTL);<br />
clkf^ := $5A000011 ; //start clock<br />
Self.ClearBit(gpin);<br />
pwmf := Pointer(LongWord(Self.FPwm)+PWM_CONTROL);<br />
pwmf^ := 0 ; // Disable PWM<br />
delayNanoseconds(200);<br />
pwmf := Pointer(LongWord(Self.FPwm)+PWM0_RANGE);<br />
pwmf^ := $400 ; //max: 1023<br />
delayNanoseconds(200);<br />
pwmf := Pointer(LongWord(Self.FPwm)+PWM1_RANGE);<br />
pwmf^ := $400 ; //max: 1023<br />
delayNanoseconds(200);<br />
// Enable PWMs<br />
pwmf := Pointer(LongWord(Self.FPwm)+PWM0_DATA);<br />
pwmf^ := 0 ; //start value<br />
pwmf := Pointer(LongWord(Self.FPwm)+PWM1_DATA);<br />
pwmf^ := 0 ; //start value<br />
pwmf := Pointer(LongWord(Self.FPwm)+PWM_CONTROL);<br />
pwmf^ := PWM0_ENABLE or PWM1_ENABLE ;<br />
end;<br />
end;<br />
end;<br />
//*******************************************************************************<br />
procedure TIoPort.SetBit(gpin : byte);<br />
var<br />
gpiof : ^LongWord;<br />
begin<br />
gpiof := Pointer(LongWord(Self.FGpio) + 28 + (gpin shr 5) shl 2);<br />
gpiof^ := 1 shl gpin;<br />
end;<br />
//*******************************************************************************<br />
procedure TIoPort.ClearBit(gpin : byte);<br />
var<br />
gpiof : ^LongWord;<br />
begin<br />
gpiof := Pointer(LongWord(Self.FGpio) + 40 + (gpin shr 5) shl 2);<br />
gpiof^ := 1 shl gpin;<br />
end;<br />
//*******************************************************************************<br />
function TIoPort.GetBit(gpin : byte):boolean;<br />
var<br />
gpiof : ^LongWord;<br />
begin<br />
gpiof := Pointer(LongWord(Self.FGpio) + 52 + (gpin shr 5) shl 2);<br />
if (gpiof^ and (1 shl gpin)) = 0 then Result := False else Result := True;<br />
end;<br />
//*******************************************************************************<br />
procedure TIoPort.SetPullMode(gpin, mode: byte);<br />
var<br />
pudf, pudclkf : ^LongWord;<br />
begin<br />
pudf := Pointer(LongWord(Self.FGpio) + 148 );<br />
pudf^ := mode; //mode = 0, 1, 2 :Off, Down, Up<br />
delayNanoseconds(200);<br />
pudclkf := Pointer(LongWord(Self.FGpio) + 152 + (gpin shr 5) shl 2);<br />
pudclkf^ := 1 shl gpin ;<br />
delayNanoseconds(200);<br />
pudf^ := 0 ;<br />
pudclkf^ := 0 ;<br />
end;<br />
//*******************************************************************************<br />
procedure TIoPort.PwmWrite(gpin : byte; value : Longword);<br />
var<br />
pwmf : ^LongWord;<br />
port : byte;<br />
begin<br />
Case gpin of<br />
12,18,40 : port:= PWM0_DATA ;<br />
13,19,41,45 : port:= PWM1_DATA ;<br />
else exit;<br />
end;<br />
pwmf := Pointer(LongWord(Self.FPwm) + port);<br />
pwmf^ := value and $FFFFFBFF; // $400 complemens<br />
end;<br />
//*******************************************************************************<br />
end. <br />
<br />
</syntaxhighlight><br />
<br />
''Controlling Lazarus unit:(Project files[https://drive.google.com/folderview?id=0B-b-5ooIWPvGU043ZTZ0cUc2YkE&usp=sharing])''<br />
<syntaxhighlight lang="pascal"><br />
unit Unit1;<br />
<br />
{Demo application for GPIO on Raspberry Pi}<br />
{Inspired by the Python input/output demo application by Gareth Halfacree}<br />
{written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5}<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
ExtCtrls, Unix, PiGpio;<br />
<br />
type<br />
<br />
{ TForm1 }<br />
<br />
TForm1 = class(TForm)<br />
GPIO25In: TButton;<br />
SpeedButton: TButton;<br />
LogMemo: TMemo;<br />
GPIO23switch: TToggleBox;<br />
Timer1: TTimer;<br />
GPIO18Pwm: TToggleBox;<br />
Direction: TToggleBox;<br />
procedure DirectionChange(Sender: TObject);<br />
procedure GPIO18PwmChange(Sender: TObject);<br />
procedure GPIO25InClick(Sender: TObject);<br />
procedure FormActivate(Sender: TObject);<br />
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
procedure GPIO23switchChange(Sender: TObject);<br />
procedure SpeedButtonClick(Sender: TObject);<br />
procedure Timer1Timer(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
const<br />
INPUT = 0;<br />
OUTPUT = 1;<br />
PWM_OUTPUT = 2;<br />
LOW = False;<br />
HIGH = True;<br />
PUD_OFF = 0;<br />
PUD_DOWN = 1;<br />
PUD_UP = 2;<br />
// Convert Raspberry Pi P1 pins (Px) to GPIO port<br />
P3 = 0;<br />
P5 = 1;<br />
P7 = 4;<br />
P8 = 14;<br />
P10 = 15;<br />
P11 = 17;<br />
P12 = 18;<br />
P13 = 21;<br />
P15 = 22;<br />
P16 = 23;<br />
P18 = 24;<br />
P19 = 10;<br />
P21 = 9;<br />
P22 = 25;<br />
P23 = 11;<br />
P24 = 8;<br />
P26 = 7;<br />
<br />
var<br />
Form1: TForm1;<br />
GPIO_Driver: TIoDriver;<br />
GpF: TIoPort;<br />
PWM :Boolean;<br />
i, d : integer;<br />
Pin,Pout,Ppwm : byte;<br />
<br />
implementation<br />
<br />
{$R *.lfm}<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.FormActivate(Sender: TObject);<br />
begin<br />
if not GPIO_Driver.MapIo then<br />
begin<br />
LogMemo.Lines.Add('Error mapping gpio registry');<br />
end<br />
else<br />
begin<br />
GpF := GpIo_Driver.CreatePort(GPIO_BASE, CLOCK_BASE, GPIO_PWM);<br />
end ;<br />
Timer1.Enabled:= True;<br />
Timer1.Interval:= 25; //25 ms controll interval<br />
Pin := P22;<br />
Pout := P16;<br />
Ppwm := P12;<br />
i:=1;<br />
GpF.SetPinMode(Pout,OUTPUT);<br />
GpF.SetPinMode(Pin,INPUT);<br />
GpF.SetPullMode(Pin,PUD_Up); // Input PullUp High level<br />
end;<br />
<br />
procedure TForm1.GPIO25InClick(Sender: TObject);<br />
begin<br />
If GpF.GetBit(Pin) then LogMemo.Lines.Add('In: '+IntToStr(1))<br />
else LogMemo.Lines.Add('In: '+IntToStr(0));<br />
end;<br />
<br />
procedure TForm1.GPIO18PwmChange(Sender: TObject);<br />
begin<br />
if GPIO18Pwm.Checked then<br />
begin<br />
GpF.SetPinMode(Ppwm,PWM_OUTPUT);<br />
PWM := True; //PWM on<br />
end<br />
else<br />
begin<br />
GpF.SetPinMode(Ppwm,INPUT);<br />
PWM := False; //PWM off<br />
end;<br />
end;<br />
<br />
procedure TForm1.DirectionChange(Sender: TObject);<br />
begin<br />
if Direction.Checked then d:=10 else d:=-10;<br />
end;<br />
<br />
<br />
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
begin<br />
GpF.SetPinMode(Ppwm,INPUT);<br />
GpF.ClearBit(Pout);<br />
GpIo_Driver.UnmapIoRegisrty(GpF);<br />
end;<br />
<br />
procedure TForm1.GPIO23switchChange(Sender: TObject);<br />
<br />
Begin<br />
Timer1.Enabled := False;<br />
if GPIO23switch.Checked then<br />
begin<br />
GpF.SetBit(Pout); //Turn LED on<br />
end<br />
else<br />
begin<br />
GpF.ClearBit(Pout); //Turn LED off<br />
end;<br />
Timer1.Enabled := True;<br />
end;<br />
<br />
procedure TForm1.SpeedButtonClick(Sender: TObject);<br />
var<br />
i,p,k,v: longint;<br />
ido:TDateTime;<br />
begin<br />
ido:= Time;<br />
k:= TimeStampToMSecs(DateTimeToTimeStamp(ido));<br />
LogMemo.Lines.Add('Start: '+TimeToStr(ido));<br />
p:=10000000 ;<br />
For i:=1 to p do Begin<br />
GpF.SetBit(P16);<br />
GpF.ClearBit(P16);<br />
end;<br />
ido:= Time;<br />
v:= TimeStampToMSecs(DateTimeToTimeStamp(ido));<br />
LogMemo.Lines.Add('Stop: '+TimeToStr(ido)+' Frequency: '+<br />
IntToStr(p div (v-k))+' kHz');<br />
end;<br />
<br />
procedure TForm1.Timer1Timer(Sender: TObject);<br />
begin<br />
If PWM then Begin<br />
If (d > 0) and (i+d < 1024) then begin<br />
i:=i+d;<br />
GpF.PwmWrite(Ppwm,i);<br />
end ;<br />
If (d < 0) and (i+d > -1) then begin<br />
i:=i+d;<br />
GpF.PwmWrite(Ppwm,i);<br />
end;<br />
end;<br />
end;<br />
<br />
<br />
end. <br />
<br />
</syntaxhighlight><br />
<br />
=== PXL (Platform eXtended Library) for low level native access to GPIO, I²C, SPI, PWM, UART, V4L2, displays and sensors ===<br />
<br />
* Easy and fast access to GPIO, I²C, SPI, PWM, UART and high precision CPU timer.<br />
* V4L2 video/image capture using onboard and USB cameras. Serial cameras such as VC0706 and LSY201.<br />
* OpenGL ES hardware rendering with and without X server. Software rendering.<br />
* I²C and SPI displays such as SSD1306, SSD1351, PCB8544 (Nokia), HX8357 and ILI9340.<br />
* Character LCDs with direct pin wiring.<br />
* Support for Software SPI and UART (bit-banging).<br />
* Support for SC16IS7x0 (UART controller connected via I²C or SPI), including extra GPIO pins.<br />
* Support for sensors such as BMP180, DHT22, L3GD20, LSM303 and SHT10.<br />
* Other features like networking, math, image transformation, effects, drawing primitives...<br />
''More info and download of [http://asphyre.net/products/pxl PXL library].''<br />
<br />
''The PinDrive property is not implemented in the latest download from Asphyre so I am posting the code for my implementation in case anyone needs it. I found that the definition for TPinDrive is incorrect for the BCM2837 chip so I added some code to change it. This could be accomplished more easily by changing the definition but that file could conceivably be used for multiple chips (and I don't control the code) so I made all changes in the code to set the drive mode.''<br />
<br />
<syntaxhighlight lang="pascal"><br />
<br />
procedure TFastGPIO.SetPinDrive(const Pin: TPinIdentifier; const Value: TPinDrive);<br />
const<br />
GPPUDadr = $94;<br />
GPPclkadr = $98;<br />
type<br />
TBCM28PinDrive = (drvNone, drvPullDown, drvPullUp);<br />
var<br />
dValue: TBCM28PinDrive;<br />
PinBCM: TPinIdentifier;<br />
GPPUD, GPPclk: Pointer;<br />
begin<br />
case Value of<br />
TPinDrive.PullUp: dValue := TBCM28PinDrive.drvPullDown;<br />
TPinDrive.PullDown: dValue := TBCM28PinDrive.drvPullUp;<br />
else<br />
dValue := TBCM28PinDrive.drvNone;<br />
<br />
PinBCM := ProcessPinNumber(Pin);<br />
<br />
GPPUD := GetOffsetPointer(GPPUDadr);<br />
WriteMemSafe(GPPUD, Cardinal(dValue));<br />
FSystemCore.MicroDelay(1);<br />
<br />
GPPclk := GetOffsetPointer(GPPclkadr + (Cardinal(PinBCM) div 32) * 4);<br />
WriteMemSafe(GPPclk, 1 shl (PinBCM mod 32));<br />
FSystemCore.MicroDelay(1);<br />
<br />
WriteMemSafe(GPPUD, 0);<br />
WriteMemSafe(GPPclk, 0);<br />
end;<br />
<br />
</syntaxhighlight><br />
<br />
== References ==<br />
* Eben Upton and Gareth Halfacree. Raspberry Pi User Guide. John Wiley and Sons, Chichester 2012, ISBN 111846446X<br />
* B. J. Rao (2013) The Raspberry Pi, Pi Vision and Lazarus/FPC. [http://www.blaisepascal.eu Blaise Pascal Magazine]. 29: 14-21.<br />
<br />
== External and Other Links ==<br />
<br />
* If you want to install the latest stable release of fpc and, additional and isolated, the trunk fpc compiler: you can read the following guide.<br />
It was written using gentoo but this guide will be useful with any distro: [[Install fpc on Raspberry with Gentoo]] (very out of date)<br />
<br />
* [http://www.raspberrypi.org Raspberry Pi Foundation]<br />
* [http://www.raspberrypi-spy.co.uk Raspberry Pi Spy: Raspberry Pi tutorials, scripts, help and downloads]<br />
* [http://www.lazarus.freepascal.org/index.php/topic,17404.0.html Lazarus wrapper unit for Gordon Henderson's wiringPi C library]<br />
* [https://github.com/laz2wiringpi/laz2wiringpi Another wiringPi wrapper with waitForInterrupt() and wiringPiISR()]<br />
* [https://forum.lazarus.freepascal.org/index.php/topic,17404.msg405237.html#msg405237 Yet another wiringPi wrapper with waitForInterrupt(), wiringPiISR(), i2c, spi and serial]<br />
* [https://projects.drogon.net/raspberry-pi/wiringpi/pins/ Pin layout of the wiringPi library]<br />
* [https://github.com/WiringPi/WiringPi wiringPi fork updated for latest hardware]<br />
* [http://www.elinux.org/Lazarus_on_RPi Additional information on Lazarus and Raspberry Pi at eLinux.org]<br />
* [http://www.pp4s.co.uk/main/gs-pi-intro.html Getting Started with Pascal on the Pi] (noted dead link in late 2023)<br />
* [http://www.pp4s.co.uk/main/gs-pi-gpio-intro.html Getting Started with Led, Switch and DC motor on Pi] (noted dead link in late 2023)<br />
* [https://github.com/laz2wiringpi/lazI2cdev lazi2cdev - General i2c with drivers for ADS1015 ADC, MCP23017 IO, MCP4725 DAC, PCA9685 PWM and HD44780 LCD]<br />
* [http://superbitysoft.co.uk/lazberrypi/ Lazberry Pi: Comprehensive information on Lazarus on the Raspberry Pi computer]. (noted dead link in late 2023)<br />
* Stefan Fischer's [https://github.com/rudiratlos/rpi-hal rpi_hal library] and [http://www.lazarus.freepascal.org/index.php/topic,20991.0.html rpi_hal discussion topic] on Lazarus forum.<br />
* [http://www.meltonisl.com/pigpio.pas Improved Lazarus Unit for fast access to GPIO].<br />
* [http://asphyre.net/products/pxl PXL - Platform eXtended Library].<br />
* [https://github.com/zipplet/rpiio RPIIO - Raspberry Pi GPIO and I2C library].</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Lazarus_on_Raspberry_Pi&diff=157995Lazarus on Raspberry Pi2023-12-31T05:07:31Z<p>Dbannon: update re Laz 3.0 release</p>
<hr />
<div>{{Lazarus_on_Raspberry_Pi}}<br />
<br />
[[File:Lazarus on Raspberry Pi Raspian Wheezy version 2012-10-28.png|thumb|200px|right|alt=Lazarus on Raspbian Wheezy.|Lazarus on Raspbian Wheezy]]<br />
<br />
{{Platform only|Raspberry Pi}}<br />
<br />
The '''Raspberry Pi''' is a credit-card-sized single-board computer. It has been developed in the UK by the Raspberry Pi Foundation with the intention of stimulating the teaching of basic computer science in schools. Raspberry Pis are also used for multiple other purposes that are as different as media servers, robotics and control engineering.<br />
<br />
The Raspberry Pi Foundation recommends [[Raspbian | the Pasperry Pi OS]] as standard operating system. Alternative systems running on RPI include RISC OS and various [[Portal:Linux|Linux]] distributions, as well as [[Portal:Android|Android]] and [[Portal:FreeBSD|FreeBSD]].<br />
<br />
Lazarus runs natively under the Raspbian operating system.<br />
<br />
==Installing and compiling Lazarus==<br />
<br />
=== Introduction ===<br />
Installing FPC and Lazarus on a Raspberry Pi is very little different from other Linux boxes, so please refer to [[Installing_Lazarus_on_Linux]]. <br />
How ever, there are some RasPi particularities worth noting.<br />
* RasPi do not always have enough memory to build Lazarus, this affects the routine rebuild of the IDE that happens when installing packages for example. The solution is to increase swap size (below).<br />
* The Lazarus SourceForge repository does not include a pre-made RasPi package. <br />
* If you are using the Raspberry Pi OS, its based on Debian and Debian has quite a long release cycle. So, at any one time, the repo provided Lazarus is likely to be out of date and, occasionally, so will the FPC be.<br />
* Notwithstanding the above point, at present, late 2023, the Debian Bookworm based Raspberry Pi OS has the appropriate FPC and a Lazarus that suits most people. This will change !<br />
<br />
=== Correcting swap file size ===<br />
(Info from forum user "Thaddy", updated.)<br />
If you have RPi with memory size less then 4Gb, and you want to build Lazarus from source or use FPCUPdeluxe or just rebuild the IDE after installing a package you need to allocate at least 1G if not 2G of swap.<br />
This appears to be particularly so with Raspberry Pi OS based on Bookworm rather than Bullseye. <br />
<br />
To increase swap you should firstly ensure you have a couple of gig free and -<br />
<br />
* sudo dphys-swapfile swapoff<br />
* sudo nano /etc/dphys-swapfile<br />
* in the file, find CONF_SWAPSIZE and change the value to 2048 (or 1024) and save it.<br />
* sudo dphys-swapfile setup<br />
* sudo dphys-swapfile swapon<br />
<br />
You should now see the file, /var/swap, increase to 2G (or 1G). On some other operating systems, eg a "pure" debian, you may need to install the dphys-swapfile package. <br />
<br />
If you don't have enough memory (real memory and swap) then, your compile processes (particularly assembling) will stop and the whole machine freezes. You have been warned !<br />
<br />
While you are fiddling with the swap file, consider, if possible, moving the swap file from /var/swap to somewhere off the SDCard, unfortunately, SDCards are not good at the repetitive writes that happen in a swap file. Not critical but a good idea.<br />
<br />
===Simple installation ===<br />
<br />
Most distributions have a graphic installer of some sort, look for entries like "Add / Remove Software" in your menus. Alternatively, all systems will have a command line interface. As hinted above, its likely that your distribution will have a current FPC, if so, best to use it. More interesting is Lazarus, as it changes more frequently, consider your needs. The Raspberry Pi OS, late 2023, has Lazarus 2.2.6. While version 3.0 of Lazarus is released now, it will take awhile to make it into distro's repos. Some users will, for the time being, be quite happy with Lazarus 2.2.6. Later on, you can remove the distro supplied Lazarus 2.2.6 and build 3.0 from source yourself, its easy.<br />
<br />
A advantage of using the distro supplied FPC and Lazarus is that they test them, make sure everything needed is there and its very easy. <br />
<br />
See [https://wiki.freepascal.org/Installing_Lazarus_on_Linux#The_Package_Manager_Model the Package Manager Model].<br />
<br />
=== Not Quite as Simple Install ===<br />
<br />
There are two other functional alternatives for installing Lazarus, building from source and using [[fpcupdeluxe]], an application that you run on your computer to manage your FPC / Lazarus install. Its very suited to people who just want to get a working install quickly, fpcupdeluxe will do the thinking for them. Fpcupdelux will install (and manage) both FPC and Lazarus and you should use them together.<br />
<br />
Installing a non distro FPC is a touch more complicated still. Its [https://wiki.freepascal.org/Installing_the_Free_Pascal_Compiler#Compiling_the_FPC_source.2C_using_preinstalled_FPC possible] to build a new FPC using an older, existing, FPC, might be a challenge for a less powerful Pi. An better option is to [https://wiki.freepascal.org/Installing_the_Free_Pascal_Compiler#Downloading_FPC_tar_balls download and install] a tarball. <br />
<br />
Its quite reasonable to use the distro provided FPC and build your own Lazarus. In fact, many users have several lazarus versions installed this way. <br />
<br />
Building Lazarus [https://wiki.freepascal.org/Installing_Lazarus_on_Linux#Build_Lazarus_from_Source from source] on a Raspberry Pi is no different from other Linuxes, except, perhaps, for the [[Lazarus_on_Raspberry_Pi#Correcting_swap_file_size | memory issue]].<br />
<br />
<gallery><br />
File:Lazarus 0 9 30 4 on RPi 2.png|Lazarus "out of the box" on Raspberry Pi 2<br />
</gallery><br />
<br />
===Cross compiling for the Raspberry Pi from Windows===<br />
1. Using fpcup<br />
<br />
One way is to use fpcup to set up a cross compiler; follow these instructions:<br />
[[fpcup#Linux_ARM_cross_compiler]]<br />
<br />
2. Using scripts<br />
<br />
Alternatively, for a more manual approach using batch files, you can follow these steps.<br />
<br />
2.1 Prerequisites<br />
FPC 2.7.1 or higher installed with source code<br />
Install the Windows version from the Linaro binutils for linux gnueabihf into %FPCPATH%/bin/win32-armhf-linux [https://launchpad.net/linaro-toolchain-binaries/trunk/2013.10/+download/gcc-linaro-arm-linux-gnueabihf-4.8-2013.10_win32.zip]<br />
<br />
2.2 Example Windows Batch file (adapt paths as needed)<br />
<br />
<syntaxhighlight lang="bat"><br />
set PATH=C:\pp\bin\i386-win32;%PATH%;<br />
set FPCMAKEPATH=C:/pp<br />
set FPCPATH=C:/pp<br />
set OUTPATH=C:/pp271<br />
%FPCMAKEPATH%/bin/i386-win32/make distclean OS_TARGET=linux CPU_TARGET=arm CROSSBINDIR=%FPCPATH%/bin/win32-armhf-linux CROSSOPT="-CpARMV6 -CfVFPV2 -OoFASTMATH" FPC=%FPCPATH%/bin/i386-win32/ppc386.exe<br />
<br />
%FPCMAKEPATH%/bin/i386-win32/make all OS_TARGET=linux CPU_TARGET=arm CROSSBINDIR=%FPCPATH%/bin/win32-armhf-linux CROSSOPT="-CpARMV6 -CfVFPV2 -OoFASTMATH" FPC=%FPCPATH%/bin/i386-win32/ppc386.exe<br />
if errorlevel 1 goto quit<br />
%FPCMAKEPATH%/bin/i386-win32/make crossinstall CROSSBINDIR=%FPCPATH%/bin/win32-armhf-linux CROSSOPT="-CpARMV6 -CfVFPV2 -OoFASTMATH" OS_TARGET=linux CPU_TARGET=arm FPC=%FPCPATH%/bin/i386-win32/ppc386.exe INSTALL_BASEDIR=%OUTPATH%<br />
<br />
:quit<br />
pause<br />
</syntaxhighlight><br />
<br />
With the resulting ppcrossarm.exe and ARM RTL you will be able to build a cross Lazarus version as usual and compile FPC projects for the Raspberry Pi and other armhf devices.<br />
<br />
Remember that not all - especially Windows - libraries are available for Linux arm.<br />
<br />
===Cross compiling for the Raspberry Pi from Linux===<br />
<br />
Please see this page https://wiki.freepascal.org/Cross_Compile_to_RasPi_from_Linux<br />
<br />
==Accessing external hardware==<br />
[[File:rpi pinout all 50.png|thumb|300px|right|alt=Raspberry Pi pinout|Raspberry Pi pinout of external connectors]]<br />
<br />
One of the goals in the development of Raspberry Pi was to facilitate effortless access to external devices like sensors and actuators. There are many ways to access the I/O facilities from Lazarus and Free Pascal:<br />
# [[Lazarus on Raspberry Pi# Native hardware access|Direct access]] using the [[BaseUnix]] unit<br />
# Access through [[Lazarus on Raspberry Pi# Hardware access via encapsulated shell calls|encapsulated shell calls]]<br />
# Access through the [[Lazarus on Raspberry Pi# wiringPi procedures and functions|wiringPi library]].<br />
# Access through Unit [[Lazarus on Raspberry Pi# rpi_hal-Hardware Abstraction Library (GPIO, I2C und SPI functions and procedures)|rpi_hal]].<br />
# Access through Unit [[Lazarus on Raspberry Pi# PiGpio Low-level native pascal unit (GPIO control instead of wiringPi c library)|PiGpio]].<br />
# Access through the [https://github.com/SAmeis/pascalio PascalIO] library.<br />
# Access through the [http://asphyre.net/products/pxl PXL] library.<br />
<br />
=== Native hardware access===<br />
[[File:testprogram for GPIO on RPI annotated.png|Simple test program for accessing the GPIO port on Raspberry Pi|thumb|300px|right]]<br />
[[File:rpi testcircuit 1d.png|Test circuit for GPIO access with the described program|thumb|300px|right]]<br />
[[File:rpi testcircuit 1p annotaded.png|Simple demo implementation of the circuit from above on a breadboard|thumb|300px|right]]<br />
This method provides access to external hardware that doesn't require additional libraries. The only requirement is the BaseUnix library that is part of Free Pascal's RTL.<br />
<br />
==== Switching a device via the GPIO port ====<br />
The following example lists a simple program that controls the GPIO pin 17 as output to switch an LED, transistor or relays. This program contains a [[doc:lcl/stdctrls/ttogglebox.html|ToggleBox]] with name ''GPIO17ToggleBox'' and for logging return codes a [[doc:lcl/stdctrls/tmemo.html|TMemo]] called ''LogMemo''.<br />
<br />
For the example, the anode of a LED has been connected with Pin 11 on the Pi's connector (corresponding to GPIO pin 17 of the BCM2835 SOC) and the LED's cathode was wired via a 68 Ohm resistor to pin 6 of the connector (GND) as previously described by Upton and Halfacree. Subsequently, the LED may be switched on and off with the application's toggle box.<br />
<br />
The code may at first require to be run as root, i.e. either from a root account (not recommended) or via '''su'''.<br />
A better option is to add the user to the gpio group, the i2c group and the spi group.<br />
<br />
<syntaxhighlight lang="bash"><br />
sudo adduser pi gpio<br />
sudo adduser pi i2c<br />
sudo adduser pi spi<br />
</syntaxhighlight><br />
<br />
''Controlling unit:''<br />
<syntaxhighlight lang="pascal"><br />
unit Unit1;<br />
<br />
{Demo application for GPIO on Raspberry Pi}<br />
{Inspired by the Python input/output demo application by Gareth Halfacree}<br />
{written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5}<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
Unix, BaseUnix;<br />
<br />
type<br />
<br />
{ TForm1 }<br />
<br />
TForm1 = class(TForm)<br />
LogMemo: TMemo;<br />
GPIO17ToggleBox: TToggleBox;<br />
procedure FormActivate(Sender: TObject);<br />
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
procedure GPIO17ToggleBoxChange(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
const<br />
PIN_17: PChar = '17';<br />
PIN_ON: PChar = '1';<br />
PIN_OFF: PChar = '0';<br />
OUT_DIRECTION: PChar = 'out';<br />
<br />
var<br />
Form1: TForm1;<br />
gReturnCode: longint; {stores the result of the IO operation}<br />
<br />
implementation<br />
<br />
{$R *.lfm}<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.FormActivate(Sender: TObject);<br />
var<br />
fileDesc: integer;<br />
begin<br />
{ Prepare SoC pin 17 (pin 11 on GPIO port) for access: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/export', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, PIN_17[0], 2);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
{ Set SoC pin 17 as output: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/gpio17/direction', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, OUT_DIRECTION[0], 3);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end;<br />
<br />
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
var<br />
fileDesc: integer;<br />
begin<br />
{ Free SoC pin 17: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/unexport', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, PIN_17[0], 2);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end;<br />
<br />
procedure TForm1.GPIO17ToggleBoxChange(Sender: TObject);<br />
var<br />
fileDesc: integer;<br />
begin<br />
if GPIO17ToggleBox.Checked then<br />
begin<br />
{ Swith SoC pin 17 on: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/gpio17/value', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, PIN_ON[0], 1);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end<br />
else<br />
begin<br />
{ Switch SoC pin 17 off: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/gpio17/value', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, PIN_OFF[0], 1);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
''Main program:''<br />
<syntaxhighlight lang="pascal"><br />
program io_test;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
uses<br />
{$IFDEF UNIX}{$IFDEF UseCThreads}<br />
cthreads,<br />
{$ENDIF}{$ENDIF}<br />
Interfaces, // this includes the LCL widgetset<br />
Forms, Unit1<br />
{ you can add units after this };<br />
<br />
{$R *.res}<br />
<br />
begin<br />
Application.Initialize;<br />
Application.CreateForm(TForm1, Form1);<br />
Application.Run;<br />
end.<br />
</syntaxhighlight><br />
<br />
==== Reading the status of a pin ====<br />
[[File:2013-04-28-193243 1280x1024 scrot.png|Demo program for reading the status of a GPIO pin|thumb|300px|right]]<br />
[[File:rpi testcircuit 2d.png|Test circuit for GPIO access with the described program|thumb|300px|right]]<br />
[[File:rpi testcircuit 2p annotaded.png|Possible implementation of this test circuit|thumb|300px|right]]<br />
<br />
Of course it is also possible to read the status of e.g. a switch that is connected to the GPIO port.<br />
<br />
The following simple example is very similar to the previous one. It controls the GPIO pin 18 as input for a binary device like a switch, transistor or relais. This program contains a [[doc:lcl/stdctrls/tcheckbox.html|CheckBox]] with name ''GPIO18CheckBox'' and for logging return codes a [[doc:lcl/stdctrls/tmemo.html|TMemo]] called ''LogMemo''.<br />
<br />
For the example, one pole of a push-button has been connected to Pin 12 on the Pi's connector (corresponding to GPIO pin 18 of the BCM2835 SOC) and via a 10k Ohm pull-up resistor with pin 1 (+3.3V, see wiring diagram). The other pole has been wired to pin 6 of the connector (GND). The program senses the status of the button and correspondingly switches the checkbox on or off, respectively.<br />
<br />
Note that the potential of pin 18 is high if the button is released (by virtue of the connection to pin 1 via the pull-up resistor) and low if it is pressed (since in this situation pin 18 is connected to GND via the switch). Therefore, the GPIO pin signals 0 if the button is pressed and 1 if it is released.<br />
<br />
This program has again to be executed as root.<br />
<br />
''Controlling unit:''<br />
<syntaxhighlight lang="pascal"><br />
unit Unit1;<br />
<br />
{Demo application for GPIO on Raspberry Pi}<br />
{Inspired by the Python input/output demo application by Gareth Halfacree}<br />
{written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5}<br />
<br />
{This application reads the status of a push-button}<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
ButtonPanel, Unix, BaseUnix;<br />
<br />
type<br />
<br />
{ TForm1 }<br />
<br />
TForm1 = class(TForm)<br />
ApplicationProperties1: TApplicationProperties;<br />
GPIO18CheckBox: TCheckBox;<br />
LogMemo: TMemo;<br />
procedure ApplicationProperties1Idle(Sender: TObject; var Done: Boolean);<br />
procedure FormActivate(Sender: TObject);<br />
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
const<br />
PIN_18: PChar = '18';<br />
IN_DIRECTION: PChar = 'in';<br />
<br />
var<br />
Form1: TForm1;<br />
gReturnCode: longint; {stores the result of the IO operation}<br />
<br />
implementation<br />
<br />
{$R *.lfm}<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.FormActivate(Sender: TObject);<br />
var<br />
fileDesc: integer;<br />
begin<br />
{ Prepare SoC pin 18 (pin 12 on GPIO port) for access: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/export', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, PIN_18[0], 2);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
{ Set SoC pin 18 as input: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/gpio18/direction', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, IN_DIRECTION[0], 2);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end;<br />
<br />
procedure TForm1.ApplicationProperties1Idle(Sender: TObject; var Done: Boolean);<br />
var<br />
fileDesc: integer;<br />
buttonStatus: string[1] = '1';<br />
begin<br />
try<br />
{ Open SoC pin 18 (pin 12 on GPIO port) in read-only mode: }<br />
fileDesc := fpopen('/sys/class/gpio/gpio18/value', O_RdOnly);<br />
if fileDesc > 0 then<br />
begin<br />
{ Read status of this pin (0: button pressed, 1: button released): }<br />
gReturnCode := fpread(fileDesc, buttonStatus[1], 1);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode) + ': ' + buttonStatus);<br />
LogMemo.SelStart := Length(LogMemo.Lines.Text) - 1;<br />
if buttonStatus = '0' then<br />
GPIO18CheckBox.Checked := true<br />
else<br />
GPIO18CheckBox.Checked := false;<br />
end;<br />
finally<br />
{ Close SoC pin 18 (pin 12 on GPIO port) }<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
LogMemo.SelStart := Length(LogMemo.Lines.Text) - 1;<br />
end;<br />
end;<br />
<br />
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
var<br />
fileDesc: integer;<br />
begin<br />
{ Free SoC pin 18: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/unexport', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, PIN_18[0], 2);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
The main program is identical to that of the example from above.<br />
<br />
=== Hardware access via encapsulated shell calls===<br />
Another way to access the hardware is by encapsulating terminal commands. This is achieved by using the [[Executing External Programs#Unix fpsystem, fpexecve and shell|fpsystem]] function. This method gives access to functions that are not supported by the BaseUnix unit. The following code implements a program that has the same functionality as the program resulting from the first listing above.<br />
<br />
''Controlling unit:''<br />
<syntaxhighlight lang="pascal"><br />
unit Unit1;<br />
<br />
{Demo application for GPIO on Raspberry Pi}<br />
{Inspired by the Python input/output demo application by Gareth Halfacree}<br />
{written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5}<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, Unix;<br />
<br />
type<br />
<br />
{ TForm1 }<br />
<br />
TForm1 = class(TForm)<br />
LogMemo: TMemo;<br />
GPIO17ToggleBox: TToggleBox;<br />
procedure FormActivate(Sender: TObject);<br />
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
procedure GPIO17ToggleBoxChange(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
var<br />
Form1: TForm1;<br />
gReturnCode: longint; {stores the result of the IO operation}<br />
<br />
implementation<br />
<br />
{$R *.lfm}<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.FormActivate(Sender: TObject);<br />
begin<br />
{ Prepare SoC pin 17 (pin 11 on GPIO port) for access: }<br />
gReturnCode := fpsystem('echo "17" > /sys/class/gpio/export');<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
{ Set SoC pin 17 as output: }<br />
gReturnCode := fpsystem('echo "out" > /sys/class/gpio/gpio17/direction');<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
<br />
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
begin<br />
{ Free SoC pin 17: }<br />
gReturnCode := fpsystem('echo "17" > /sys/class/gpio/unexport');<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
<br />
procedure TForm1.GPIO17ToggleBoxChange(Sender: TObject);<br />
begin<br />
if GPIO17ToggleBox.Checked then<br />
begin<br />
{ Swith SoC pin 17 on: }<br />
gReturnCode := fpsystem('echo "1" > /sys/class/gpio/gpio17/value');<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end<br />
else<br />
begin<br />
{ Switch SoC pin 17 off: }<br />
gReturnCode := fpsystem('echo "0" > /sys/class/gpio/gpio17/value');<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
The main program is identical to that of the example above. This program has to be executed with root privileges, too.<br />
<br />
=== wiringPi procedures and functions===<br />
<br />
Alex Schaller's wrapper unit for Gordon Henderson's Arduino compatible wiringPi library provides a numbering scheme that resembles that of Arduino boards.<br />
<br />
'''Function wiringPiSetup:longint:''' Initializes wiringPi system using the wiringPi pin numbering scheme.<br />
<br />
'''Procedure wiringPiGpioMode(mode:longint):''' Initializes wiringPi system with the Broadcom GPIO pin numbering scheme.<br />
<br />
'''Procedure pullUpDnControl(pin:longint;''' pud:longint): controls the internal pull-up/down resistors on a GPIO pin.<br />
<br />
'''Procedure pinMode(pin:longint; mode:longint):''' sets the mode of a pin to either INPUT, OUTPUT, or PWM_OUTPUT.<br />
<br />
'''Procedure digitalWrite(pin:longint; value:longint):''' sets an output bit.<br />
<br />
'''Procedure pwmWrite(pin:longint; value:longint):''' sets an output PWM value between 0 and 1024.<br />
<br />
'''Function digitalRead(pin:longint):longint:''' reads the value of a given Pin, returning 1 or 0.<br />
<br />
'''Procedure delay(howLong:dword):''' waits for at least howLong milliseconds.<br />
<br />
'''Procedure delayMicroseconds(howLong:dword):''' waits for at least howLong microseconds.<br />
<br />
'''Function millis:dword:''' returns the number of milliseconds since the program called one of the wiringPiSetup functions.<br />
<br />
=== rpi_hal-Hardware Abstraction Library (GPIO, I2C and SPI functions and procedures)===<br />
<br />
This Unit with around 1700 Lines of Code provided by Stefan Fischer, delivers procedures and functions to access the rpi HW I2C, SPI and GPIO:<br />
<br />
Just an excerpt of the available functions and procedures:<br />
<br />
'''procedure gpio_set_pin (pin:longword;highlevel:boolean);''' { Set RPi GPIO pin to high or low level; Speed @ 700MHz -> 0.65MHz }<br />
<br />
'''function gpio_get_PIN (pin:longword):boolean;''' { Get RPi GPIO pin Level is true when Pin level is '1'; false when '0'; Speed @ 700MHz -> 1.17MHz } <br />
<br />
'''procedure gpio_set_input (pin:longword);''' { Set RPi GPIO pin to input direction }<br />
<br />
'''procedure gpio_set_output(pin:longword);''' { Set RPi GPIO pin to output direction }<br />
<br />
'''procedure gpio_set_alt (pin,altfunc:longword);''' { Set RPi GPIO pin to alternate function nr. 0..5 }<br />
<br />
'''procedure gpio_set_gppud (mask:longword);''' { set RPi GPIO Pull-up/down Register (GPPUD) with mask }<br />
<br />
...<br />
<br />
'''function rpi_snr :string;''' { delivers SNR: 0000000012345678 }<br />
<br />
'''function rpi_hw :string;''' { delivers Processor Type: BCM2708 }<br />
<br />
'''function rpi_proc:string;''' { ARMv6-compatible processor rev 7 (v6l) }<br />
<br />
...<br />
<br />
'''function i2c_bus_write(baseadr,reg:word; var data:databuf_t; lgt:byte; testnr:integer) : integer;''' <br />
<br />
'''function i2c_bus_read (baseadr,reg:word; var data:databuf_t; lgt:byte; testnr:integer) : integer;'''<br />
<br />
'''function i2c_string_read(baseadr,reg:word; var data:databuf_t; lgt:byte; testnr:integer) : string;''' <br />
<br />
'''function i2c_string_write(baseadr,reg:word; s:string; testnr:integer) : integer;'''<br />
<br />
...<br />
<br />
'''procedure SPI_Write(devnum:byte; reg,data:word);'''<br />
<br />
'''function SPI_Read(devnum:byte; reg:word) : byte;'''<br />
<br />
'''procedure SPI_BurstRead2Buffer (devnum,start_reg:byte; xferlen:longword);'''<br />
<br />
'''procedure SPI_BurstWriteBuffer (devnum,start_reg:byte; xferlen:longword);''' { Write 'len' Bytes from Buffer SPI Dev startig at address 'reg' }<br />
<br />
... <br />
<br />
<br />
''Test Program (testrpi.pas):''<br />
<syntaxhighlight lang="pascal"><br />
//Simple Test program, which is using rpi_hal;<br />
<br />
program testrpi;<br />
uses rpi_hal;<br />
begin<br />
writeln('Show CPU-Info, RPI-HW-Info and Registers:');<br />
rpi_show_all_info;<br />
writeln('Let Status LED Blink. Using GPIO functions:');<br />
GPIO_PIN_TOGGLE_TEST;<br />
writeln('Test SPI Read function. (piggy back board with installed RFM22B Module is required!)');<br />
Test_SPI;<br />
end.<br />
<br />
</syntaxhighlight><br />
<br />
=== PiGpio Low-level native pascal unit (GPIO control instead of wiringPi c library)===<br />
<br />
''This Unit (pigpio.pas[https://docs.google.com/file/d/0B-b-5ooIWPvGN1BIcEJzRDgyeUE/edit?usp=sharing]) with 270 Lines of Code provided by Gabor Szollosi, works very fast (for ex. GPIO pin output switching frequency 8 MHz) :''<br />
<syntaxhighlight lang="pascal"><br />
<br />
unit PiGpio;<br />
{<br />
BCM2835 GPIO Registry Driver, also can use to manipulate cpu other registry areas<br />
<br />
This code is tested only Broadcom bcm2835 cpu, different arm cpus may need different<br />
gpio driver implementation<br />
<br />
2013 Gabor Szollosi<br />
}<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils;<br />
<br />
const<br />
REG_GPIO = $3F000;// bcm2835 gpio register 0x2000 0000 (RPi1), 0x3F00 0000 (RPi2)<br />
// new fpMap uses page offset, one page is 4096 bytes = 0x1000 so simply calculate 0x2000 0000 / 0x1000 = 0x2000 0<br />
PAGE_SIZE = 4096;<br />
BLOCK_SIZE = 4096;<br />
// The BCM2835 has 54 GPIO pins.<br />
// BCM2835 data sheet, Page 90 onwards.<br />
// There are 6 control registers, each control the functions of a block<br />
// of 10 pins.<br />
<br />
CLOCK_BASE = (REG_GPIO + $101);<br />
GPIO_BASE = (REG_GPIO + $200);<br />
GPIO_PWM = (REG_GPIO + $20C);<br />
<br />
INPUT = 0;<br />
OUTPUT = 1;<br />
PWM_OUTPUT = 2;<br />
LOW = False;<br />
HIGH = True;<br />
PUD_OFF = 0;<br />
PUD_DOWN = 1;<br />
PUD_UP = 2;<br />
<br />
// PWM<br />
<br />
PWM_CONTROL = 0;<br />
PWM_STATUS = 4;<br />
PWM0_RANGE = 16;<br />
PWM0_DATA = 20;<br />
PWM1_RANGE = 32;<br />
PWM1_DATA = 36;<br />
<br />
PWMCLK_CNTL = 160;<br />
PWMCLK_DIV = 164;<br />
<br />
PWM1_MS_MODE = $8000; // Run in MS mode<br />
PWM1_USEFIFO = $2000; // Data from FIFO<br />
PWM1_REVPOLAR = $1000; // Reverse polarity<br />
PWM1_OFFSTATE = $0800; // Ouput Off state<br />
PWM1_REPEATFF = $0400; // Repeat last value if FIFO empty<br />
PWM1_SERIAL = $0200; // Run in serial mode<br />
PWM1_ENABLE = $0100; // Channel Enable<br />
<br />
PWM0_MS_MODE = $0080; // Run in MS mode<br />
PWM0_USEFIFO = $0020; // Data from FIFO<br />
PWM0_REVPOLAR = $0010; // Reverse polarity<br />
PWM0_OFFSTATE = $0008; // Ouput Off state<br />
PWM0_REPEATFF = $0004; // Repeat last value if FIFO empty<br />
PWM0_SERIAL = $0002; // Run in serial mode<br />
PWM0_ENABLE = $0001; // Channel Enable<br />
<br />
<br />
type<br />
<br />
{ TIoPort }<br />
<br />
TIoPort = class // IO bank object<br />
private //<br />
<br />
//function get_pinDirection(aPin: TGpIoPin): TGpioPinConf;<br />
<br />
public<br />
FGpio: ^LongWord;<br />
FClk: ^LongWord;<br />
FPwm: ^LongWord;<br />
procedure SetPinMode(gpin, mode: byte);<br />
function GetBit(gpin : byte):boolean;inline; // gets pin bit}<br />
procedure ClearBit(gpin : byte);inline;// write pin to 0<br />
procedure SetBit(gpin : byte);inline;// write pin to 1<br />
procedure SetPullMode(gpin, mode: byte);<br />
procedure PwmWrite(gpin : byte; value : LongWord);inline;// write pin to pwm value<br />
end;<br />
<br />
{ TIoDriver }<br />
<br />
TIoDriver = class<br />
private<br />
<br />
public<br />
destructor Destroy;override;<br />
function MapIo:boolean;// creates io memory mapping<br />
procedure UnmapIoRegisrty(FMap: TIoPort);// close io memory mapping<br />
function CreatePort(PortGpio, PortClk, PortPwm: LongWord):TIoPort; // create new IO port<br />
end;<br />
<br />
var<br />
<br />
fd: integer;// /dev/mem file handle<br />
procedure delayNanoseconds (howLong : LongWord);<br />
<br />
<br />
implementation<br />
<br />
uses<br />
baseUnix, Unix;<br />
<br />
procedure delayNanoseconds (howLong : LongWord);<br />
var<br />
sleeper, dummy : timespec;<br />
begin<br />
sleeper.tv_sec := 0 ;<br />
sleeper.tv_nsec := howLong ;<br />
fpnanosleep (@sleeper,@dummy) ;<br />
end;<br />
{ TIoDriver }<br />
//*******************************************************************************<br />
destructor TIoDriver.Destroy;<br />
begin<br />
inherited Destroy;<br />
end;<br />
//*******************************************************************************<br />
function TIoDriver.MapIo: boolean;<br />
begin<br />
Result := True;<br />
fd := fpopen('/dev/mem', O_RdWr or O_Sync); // Open the master /dev/memory device<br />
if fd < 0 then<br />
begin<br />
Result := False; // unsuccessful memory mapping<br />
end;<br />
//<br />
end;<br />
//*******************************************************************************<br />
procedure TIoDriver.UnmapIoRegisrty(FMap:TIoPort);<br />
begin<br />
if FMap.FGpio <> nil then<br />
begin<br />
fpMUnmap(FMap.FGpio,PAGE_SIZE);<br />
FMap.FGpio := Nil;<br />
end;<br />
if FMap.FClk <> nil then<br />
begin<br />
fpMUnmap(FMap.FClk,PAGE_SIZE);<br />
FMap.FClk := Nil;<br />
end;<br />
if FMap.FPwm <> nil then<br />
begin<br />
fpMUnmap(FMap.FPwm ,PAGE_SIZE);<br />
FMap.FPwm := Nil;<br />
end;<br />
end;<br />
//*******************************************************************************<br />
function TIoDriver.CreatePort(PortGpio, PortClk, PortPwm: LongWord): TIoPort;<br />
begin<br />
Result := TIoPort.Create;// new io port, pascal calls new fpMap, where offst is page sized 4096 bytes!!!<br />
Result.FGpio := FpMmap(Nil, PAGE_SIZE, PROT_READ or PROT_WRITE, MAP_SHARED, fd, PortGpio); // port config gpio memory<br />
Result.FClk:= FpMmap(Nil, PAGE_SIZE, PROT_READ or PROT_WRITE, MAP_SHARED, fd, PortClk);; // port clk<br />
Result.FPwm:= FpMmap(Nil, PAGE_SIZE, PROT_READ or PROT_WRITE, MAP_SHARED, fd, PortPwm);; // port pwm<br />
end;<br />
//*******************************************************************************<br />
procedure TIoPort.SetPinMode(gpin, mode: byte);<br />
var<br />
fSel, shift, alt : byte;<br />
gpiof, clkf, pwmf : ^LongWord;<br />
begin<br />
fSel := (gpin div 10)*4 ; //Select Gpfsel 0 to 5 register<br />
shift := (gpin mod 10)*3 ; //0-9 pin shift<br />
gpiof := Pointer(LongWord(Self.FGpio)+fSel);<br />
if (mode = INPUT) then<br />
gpiof^ := gpiof^ and ($FFFFFFFF - (7 shl shift)) //7 shl shift komplemens - Sets bits to zero = input<br />
else if (mode = OUTPUT) then<br />
begin<br />
gpiof^ := gpiof^ and ($FFFFFFFF - (7 shl shift)) or (1 shl shift);<br />
end<br />
else if (mode = PWM_OUTPUT) then<br />
begin<br />
Case gpin of<br />
12,13,40,41,45 : alt:= 4 ;<br />
18,19 : alt:= 2 ;<br />
else alt:= 0 ;<br />
end;<br />
If alt > 0 then<br />
begin<br />
gpiof^ := gpiof^ and ($FFFFFFFF - (7 shl shift)) or (alt shl shift);<br />
clkf := Pointer(LongWord(Self.FClk)+PWMCLK_CNTL);<br />
clkf^ := $5A000011 or (1 shl 5) ; //stop clock<br />
delayNanoseconds(200);<br />
clkf := Pointer(LongWord(Self.FClk)+PWMCLK_DIV);<br />
clkf^ := $5A000000 or (32 shl 12) ; // set pwm clock div to 32 (19.2/3 = 600KHz)<br />
clkf := Pointer(LongWord(Self.FClk)+PWMCLK_CNTL);<br />
clkf^ := $5A000011 ; //start clock<br />
Self.ClearBit(gpin);<br />
pwmf := Pointer(LongWord(Self.FPwm)+PWM_CONTROL);<br />
pwmf^ := 0 ; // Disable PWM<br />
delayNanoseconds(200);<br />
pwmf := Pointer(LongWord(Self.FPwm)+PWM0_RANGE);<br />
pwmf^ := $400 ; //max: 1023<br />
delayNanoseconds(200);<br />
pwmf := Pointer(LongWord(Self.FPwm)+PWM1_RANGE);<br />
pwmf^ := $400 ; //max: 1023<br />
delayNanoseconds(200);<br />
// Enable PWMs<br />
pwmf := Pointer(LongWord(Self.FPwm)+PWM0_DATA);<br />
pwmf^ := 0 ; //start value<br />
pwmf := Pointer(LongWord(Self.FPwm)+PWM1_DATA);<br />
pwmf^ := 0 ; //start value<br />
pwmf := Pointer(LongWord(Self.FPwm)+PWM_CONTROL);<br />
pwmf^ := PWM0_ENABLE or PWM1_ENABLE ;<br />
end;<br />
end;<br />
end;<br />
//*******************************************************************************<br />
procedure TIoPort.SetBit(gpin : byte);<br />
var<br />
gpiof : ^LongWord;<br />
begin<br />
gpiof := Pointer(LongWord(Self.FGpio) + 28 + (gpin shr 5) shl 2);<br />
gpiof^ := 1 shl gpin;<br />
end;<br />
//*******************************************************************************<br />
procedure TIoPort.ClearBit(gpin : byte);<br />
var<br />
gpiof : ^LongWord;<br />
begin<br />
gpiof := Pointer(LongWord(Self.FGpio) + 40 + (gpin shr 5) shl 2);<br />
gpiof^ := 1 shl gpin;<br />
end;<br />
//*******************************************************************************<br />
function TIoPort.GetBit(gpin : byte):boolean;<br />
var<br />
gpiof : ^LongWord;<br />
begin<br />
gpiof := Pointer(LongWord(Self.FGpio) + 52 + (gpin shr 5) shl 2);<br />
if (gpiof^ and (1 shl gpin)) = 0 then Result := False else Result := True;<br />
end;<br />
//*******************************************************************************<br />
procedure TIoPort.SetPullMode(gpin, mode: byte);<br />
var<br />
pudf, pudclkf : ^LongWord;<br />
begin<br />
pudf := Pointer(LongWord(Self.FGpio) + 148 );<br />
pudf^ := mode; //mode = 0, 1, 2 :Off, Down, Up<br />
delayNanoseconds(200);<br />
pudclkf := Pointer(LongWord(Self.FGpio) + 152 + (gpin shr 5) shl 2);<br />
pudclkf^ := 1 shl gpin ;<br />
delayNanoseconds(200);<br />
pudf^ := 0 ;<br />
pudclkf^ := 0 ;<br />
end;<br />
//*******************************************************************************<br />
procedure TIoPort.PwmWrite(gpin : byte; value : Longword);<br />
var<br />
pwmf : ^LongWord;<br />
port : byte;<br />
begin<br />
Case gpin of<br />
12,18,40 : port:= PWM0_DATA ;<br />
13,19,41,45 : port:= PWM1_DATA ;<br />
else exit;<br />
end;<br />
pwmf := Pointer(LongWord(Self.FPwm) + port);<br />
pwmf^ := value and $FFFFFBFF; // $400 complemens<br />
end;<br />
//*******************************************************************************<br />
end. <br />
<br />
</syntaxhighlight><br />
<br />
''Controlling Lazarus unit:(Project files[https://drive.google.com/folderview?id=0B-b-5ooIWPvGU043ZTZ0cUc2YkE&usp=sharing])''<br />
<syntaxhighlight lang="pascal"><br />
unit Unit1;<br />
<br />
{Demo application for GPIO on Raspberry Pi}<br />
{Inspired by the Python input/output demo application by Gareth Halfacree}<br />
{written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5}<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
ExtCtrls, Unix, PiGpio;<br />
<br />
type<br />
<br />
{ TForm1 }<br />
<br />
TForm1 = class(TForm)<br />
GPIO25In: TButton;<br />
SpeedButton: TButton;<br />
LogMemo: TMemo;<br />
GPIO23switch: TToggleBox;<br />
Timer1: TTimer;<br />
GPIO18Pwm: TToggleBox;<br />
Direction: TToggleBox;<br />
procedure DirectionChange(Sender: TObject);<br />
procedure GPIO18PwmChange(Sender: TObject);<br />
procedure GPIO25InClick(Sender: TObject);<br />
procedure FormActivate(Sender: TObject);<br />
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
procedure GPIO23switchChange(Sender: TObject);<br />
procedure SpeedButtonClick(Sender: TObject);<br />
procedure Timer1Timer(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
const<br />
INPUT = 0;<br />
OUTPUT = 1;<br />
PWM_OUTPUT = 2;<br />
LOW = False;<br />
HIGH = True;<br />
PUD_OFF = 0;<br />
PUD_DOWN = 1;<br />
PUD_UP = 2;<br />
// Convert Raspberry Pi P1 pins (Px) to GPIO port<br />
P3 = 0;<br />
P5 = 1;<br />
P7 = 4;<br />
P8 = 14;<br />
P10 = 15;<br />
P11 = 17;<br />
P12 = 18;<br />
P13 = 21;<br />
P15 = 22;<br />
P16 = 23;<br />
P18 = 24;<br />
P19 = 10;<br />
P21 = 9;<br />
P22 = 25;<br />
P23 = 11;<br />
P24 = 8;<br />
P26 = 7;<br />
<br />
var<br />
Form1: TForm1;<br />
GPIO_Driver: TIoDriver;<br />
GpF: TIoPort;<br />
PWM :Boolean;<br />
i, d : integer;<br />
Pin,Pout,Ppwm : byte;<br />
<br />
implementation<br />
<br />
{$R *.lfm}<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.FormActivate(Sender: TObject);<br />
begin<br />
if not GPIO_Driver.MapIo then<br />
begin<br />
LogMemo.Lines.Add('Error mapping gpio registry');<br />
end<br />
else<br />
begin<br />
GpF := GpIo_Driver.CreatePort(GPIO_BASE, CLOCK_BASE, GPIO_PWM);<br />
end ;<br />
Timer1.Enabled:= True;<br />
Timer1.Interval:= 25; //25 ms controll interval<br />
Pin := P22;<br />
Pout := P16;<br />
Ppwm := P12;<br />
i:=1;<br />
GpF.SetPinMode(Pout,OUTPUT);<br />
GpF.SetPinMode(Pin,INPUT);<br />
GpF.SetPullMode(Pin,PUD_Up); // Input PullUp High level<br />
end;<br />
<br />
procedure TForm1.GPIO25InClick(Sender: TObject);<br />
begin<br />
If GpF.GetBit(Pin) then LogMemo.Lines.Add('In: '+IntToStr(1))<br />
else LogMemo.Lines.Add('In: '+IntToStr(0));<br />
end;<br />
<br />
procedure TForm1.GPIO18PwmChange(Sender: TObject);<br />
begin<br />
if GPIO18Pwm.Checked then<br />
begin<br />
GpF.SetPinMode(Ppwm,PWM_OUTPUT);<br />
PWM := True; //PWM on<br />
end<br />
else<br />
begin<br />
GpF.SetPinMode(Ppwm,INPUT);<br />
PWM := False; //PWM off<br />
end;<br />
end;<br />
<br />
procedure TForm1.DirectionChange(Sender: TObject);<br />
begin<br />
if Direction.Checked then d:=10 else d:=-10;<br />
end;<br />
<br />
<br />
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
begin<br />
GpF.SetPinMode(Ppwm,INPUT);<br />
GpF.ClearBit(Pout);<br />
GpIo_Driver.UnmapIoRegisrty(GpF);<br />
end;<br />
<br />
procedure TForm1.GPIO23switchChange(Sender: TObject);<br />
<br />
Begin<br />
Timer1.Enabled := False;<br />
if GPIO23switch.Checked then<br />
begin<br />
GpF.SetBit(Pout); //Turn LED on<br />
end<br />
else<br />
begin<br />
GpF.ClearBit(Pout); //Turn LED off<br />
end;<br />
Timer1.Enabled := True;<br />
end;<br />
<br />
procedure TForm1.SpeedButtonClick(Sender: TObject);<br />
var<br />
i,p,k,v: longint;<br />
ido:TDateTime;<br />
begin<br />
ido:= Time;<br />
k:= TimeStampToMSecs(DateTimeToTimeStamp(ido));<br />
LogMemo.Lines.Add('Start: '+TimeToStr(ido));<br />
p:=10000000 ;<br />
For i:=1 to p do Begin<br />
GpF.SetBit(P16);<br />
GpF.ClearBit(P16);<br />
end;<br />
ido:= Time;<br />
v:= TimeStampToMSecs(DateTimeToTimeStamp(ido));<br />
LogMemo.Lines.Add('Stop: '+TimeToStr(ido)+' Frequency: '+<br />
IntToStr(p div (v-k))+' kHz');<br />
end;<br />
<br />
procedure TForm1.Timer1Timer(Sender: TObject);<br />
begin<br />
If PWM then Begin<br />
If (d > 0) and (i+d < 1024) then begin<br />
i:=i+d;<br />
GpF.PwmWrite(Ppwm,i);<br />
end ;<br />
If (d < 0) and (i+d > -1) then begin<br />
i:=i+d;<br />
GpF.PwmWrite(Ppwm,i);<br />
end;<br />
end;<br />
end;<br />
<br />
<br />
end. <br />
<br />
</syntaxhighlight><br />
<br />
=== PXL (Platform eXtended Library) for low level native access to GPIO, I²C, SPI, PWM, UART, V4L2, displays and sensors ===<br />
<br />
* Easy and fast access to GPIO, I²C, SPI, PWM, UART and high precision CPU timer.<br />
* V4L2 video/image capture using onboard and USB cameras. Serial cameras such as VC0706 and LSY201.<br />
* OpenGL ES hardware rendering with and without X server. Software rendering.<br />
* I²C and SPI displays such as SSD1306, SSD1351, PCB8544 (Nokia), HX8357 and ILI9340.<br />
* Character LCDs with direct pin wiring.<br />
* Support for Software SPI and UART (bit-banging).<br />
* Support for SC16IS7x0 (UART controller connected via I²C or SPI), including extra GPIO pins.<br />
* Support for sensors such as BMP180, DHT22, L3GD20, LSM303 and SHT10.<br />
* Other features like networking, math, image transformation, effects, drawing primitives...<br />
''More info and download of [http://asphyre.net/products/pxl PXL library].''<br />
<br />
''The PinDrive property is not implemented in the latest download from Asphyre so I am posting the code for my implementation in case anyone needs it. I found that the definition for TPinDrive is incorrect for the BCM2837 chip so I added some code to change it. This could be accomplished more easily by changing the definition but that file could conceivably be used for multiple chips (and I don't control the code) so I made all changes in the code to set the drive mode.''<br />
<br />
<syntaxhighlight lang="pascal"><br />
<br />
procedure TFastGPIO.SetPinDrive(const Pin: TPinIdentifier; const Value: TPinDrive);<br />
const<br />
GPPUDadr = $94;<br />
GPPclkadr = $98;<br />
type<br />
TBCM28PinDrive = (drvNone, drvPullDown, drvPullUp);<br />
var<br />
dValue: TBCM28PinDrive;<br />
PinBCM: TPinIdentifier;<br />
GPPUD, GPPclk: Pointer;<br />
begin<br />
case Value of<br />
TPinDrive.PullUp: dValue := TBCM28PinDrive.drvPullDown;<br />
TPinDrive.PullDown: dValue := TBCM28PinDrive.drvPullUp;<br />
else<br />
dValue := TBCM28PinDrive.drvNone;<br />
<br />
PinBCM := ProcessPinNumber(Pin);<br />
<br />
GPPUD := GetOffsetPointer(GPPUDadr);<br />
WriteMemSafe(GPPUD, Cardinal(dValue));<br />
FSystemCore.MicroDelay(1);<br />
<br />
GPPclk := GetOffsetPointer(GPPclkadr + (Cardinal(PinBCM) div 32) * 4);<br />
WriteMemSafe(GPPclk, 1 shl (PinBCM mod 32));<br />
FSystemCore.MicroDelay(1);<br />
<br />
WriteMemSafe(GPPUD, 0);<br />
WriteMemSafe(GPPclk, 0);<br />
end;<br />
<br />
</syntaxhighlight><br />
<br />
== References ==<br />
* Eben Upton and Gareth Halfacree. Raspberry Pi User Guide. John Wiley and Sons, Chichester 2012, ISBN 111846446X<br />
* B. J. Rao (2013) The Raspberry Pi, Pi Vision and Lazarus/FPC. [http://www.blaisepascal.eu Blaise Pascal Magazine]. 29: 14-21.<br />
<br />
== External and Other Links ==<br />
<br />
* If you want to install the latest stable release of fpc and, additional and isolated, the trunk fpc compiler: you can read the following guide.<br />
It was written using gentoo but this guide will be useful with any distro: [[Install fpc on Raspberry with Gentoo]] (very out of date)<br />
<br />
* [http://www.raspberrypi.org Raspberry Pi Foundation]<br />
* [http://www.raspberrypi-spy.co.uk Raspberry Pi Spy: Raspberry Pi tutorials, scripts, help and downloads]<br />
* [http://www.lazarus.freepascal.org/index.php/topic,17404.0.html Lazarus wrapper unit for Gordon Henderson's wiringPi C library]<br />
* [https://github.com/laz2wiringpi/laz2wiringpi Another wiringPi wrapper with waitForInterrupt() and wiringPiISR()]<br />
* [https://forum.lazarus.freepascal.org/index.php/topic,17404.msg405237.html#msg405237 Yet another wiringPi wrapper with waitForInterrupt(), wiringPiISR(), i2c, spi and serial]<br />
* [https://projects.drogon.net/raspberry-pi/wiringpi/pins/ Pin layout of the wiringPi library]<br />
* [https://github.com/WiringPi/WiringPi wiringPi fork updated for latest hardware]<br />
* [http://www.elinux.org/Lazarus_on_RPi Additional information on Lazarus and Raspberry Pi at eLinux.org]<br />
* [http://www.pp4s.co.uk/main/gs-pi-intro.html Getting Started with Pascal on the Pi] (noted dead link in late 2023)<br />
* [http://www.pp4s.co.uk/main/gs-pi-gpio-intro.html Getting Started with Led, Switch and DC motor on Pi] (noted dead link in late 2023)<br />
* [https://github.com/laz2wiringpi/lazI2cdev lazi2cdev - General i2c with drivers for ADS1015 ADC, MCP23017 IO, MCP4725 DAC, PCA9685 PWM and HD44780 LCD]<br />
* [http://superbitysoft.co.uk/lazberrypi/ Lazberry Pi: Comprehensive information on Lazarus on the Raspberry Pi computer]. (noted dead link in late 2023)<br />
* Stefan Fischer's [https://github.com/rudiratlos/rpi-hal rpi_hal library] and [http://www.lazarus.freepascal.org/index.php/topic,20991.0.html rpi_hal discussion topic] on Lazarus forum.<br />
* [http://www.meltonisl.com/pigpio.pas Improved Lazarus Unit for fast access to GPIO].<br />
* [http://asphyre.net/products/pxl PXL - Platform eXtended Library].<br />
* [https://github.com/zipplet/rpiio RPIIO - Raspberry Pi GPIO and I2C library].</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Installing_Lazarus_on_Linux&diff=157978Installing Lazarus on Linux2023-12-23T02:57:28Z<p>Dbannon: update link to Lazarus current</p>
<hr />
<div><br />
<br />
== Introduction ==<br />
<br />
This page is about installing FPC and Lazarus on a Linux system. Its intended for new and 'regular' users, it does not cover some very advanced methods and concentrates on current releases. Linux users should think of FPC and Lazarus as being two distinct but related processes, FPC does not change much, on the other hand, you will probably want to update your Lazarus install or perhaps install multiple Lazarus versions. Mixing the install methods, especially after V2.0 may cause problems so please read carefully.<br />
<br />
'''Notes about terms used on this page -''' <br />
* ''We refer to various downloadable files such as '''fpc_something''' - you should replace the 'something' with whats appropriate for your system, for example, early 2019 using a DEB based 64bit distro it might be '''fpc-laz_3.0.4-1_amd64.deb'''. Mid 2021 lazarus-something.deb is '''lazarus-project_2.0.12-0_amd64.deb'''.''<br />
* '''''Package Manager''' means a range of things on different different Linux systems. While we give command line examples, thats just because its easier to write. You will get the same result if you use your favourite GUI Tool such as '''Synaptic''', '''Ubuntu Software Centre''' or '''YaST2'''. Maybe try double clicking on a downloaded package in your file manager.''<br />
* ''What ever Package manager tool you use, make sure you are using one that resolves '''dependencies''' (that is, it also installs thing that are required by the main package you are installing), most GUI apps do. You should avoid using tools like dpkg or rpm and instead use apt or yum depending on your flavour of Linux.''<br />
<br />
== Required Linux packages ==<br />
<br />
According to forum member "MarkMLl", required Debian packages for Lazarus are:<br />
<br />
* build-essential<br />
* gdb (see below)<br />
* libgpm-dev (formerly libgpmg1-dev)<br />
* libncurses5-dev<br />
* libncursesw5-dev<br />
<br />
Additionally, some distribution may also require appmenu-gtk2-module. If you find starting Lazarus has a 20 odd second delay, do try this additional package. Seems to be a particular problem on Ubuntu.<br />
<br />
=== Debugger ===<br />
<br />
Linux (and Windows) users on Intel or AMD processors can use fpdebug backend (or, more correctly, FPDebug internal Dwarf-Debugger) and for most users thats a good choice. Its reported to be faster and in some respects, more feature rich than gdb. And its bundled in with Lazarus.<br />
<br />
If its not auto selected (perhaps because you also have gdb installed), in Lazarus, Tool->Options->Debugger->Debugger-Backend. Click the "Change Type" button and select "FPDebug internal Dwarf-Debugger".<br />
<br />
Other platforms, or perhaps users with specific needs ( see https://forum.lazarus.freepascal.org/index.php/topic,55131.msg417949.html#msg417949 ) will need to stick with gdb.<br />
<br />
== Make a Choice ==<br />
<br />
You do need to decide, early on, where you will be getting your Lazarus install from. You can, at any stage change your mind but at the cost of some backtracking. We don't recomend you get your FPC and Lazarus install kits from different places, its sometimes possible but sometimes unreliable. Broadly, your choices are -<br />
<br />
* Use your '''Package Manager''' to install both FPC and Lazarus. This means you are locked into the version of Lazarus that your distribution maintainers offer. Due to distro and Lazarus release cycles being out of sync, at any one time, its unlikely that your distribution will have the latest release of Lazarus. And many users do find those new releases attractive. You can install only one version of Lazarus under this model. But on the other hand, using the package manager approach is very easy and probably suits casual users.<br />
<br />
* Use the pre compiled packages (both FPC and Lazarus) made available by the FPC/Lazarus Team at '''SourceForge'''. Here you will find a wide choice of matched versions but you can only install one version at a time. The install process is still very easy ! This is possibly the best bet for new users.<br />
<br />
* Install FPC from either your Package Manager or SourceForge and then download the Lazarus '''source code and compile''' it yourself. May take a touch longer but is almost as easy and you do end up with the ability to add extra Lazarus installs, perhaps a 'production' version and a version based on the next release candidate. Or an old version to work on legacy code. In addition, you can easily make your own changes to the Lazarus Component Library, maybe just debug statements, maybe additional features you can feed back to help improve Lazarus further.<br />
<br />
* Use '''FPCUPdeluxe''' - it can install a range of versions of both FPC and Lazarus for you without requiring you to understand the underlying structure.<br />
<br />
=== The Package Manager Model ===<br />
<br />
If you are happy with the versions offered by your distribution this might be the way to go. Almost all Linux distributions come with some sort of Package Manager, you are probably familiar with your's. Between then, the DEB and RPM based package systems represent much of the Linux community. And if you soon run into the limitations of this model, your package manager will help you back out again. But first, check the version available. Look up 'lazarus' in your GUI Package Manager or do -<br />
<syntaxhighlight lang="pascal"><br />
// DEB Users -<br />
apt-cache policy lazarus<br />
<br />
// RPM User -<br />
yum search lazarus<br />
</syntaxhighlight><br />
<br />
Note the names as well as the versions, we need three packages, fpc-something, fpc-source-something and, finally lazarus_something. Assuming you distribution's package has its dependencies set up correctly, all you need do is choose to install Lazarus in either your Package Manage GUI or do: <br />
<syntaxhighlight lang="pascal">// DEB Users -<br />
sudo apt-get install lazarus<br />
<br />
// RPM Users -<br />
sudo yum install lazarus<br />
<br />
// or<br />
sudo dnf install lazarus<br />
</syntaxhighlight><br />
<br />
'''Notes :'''<br />
* Some distro prepared packages do not include the tools necessary to add cross compiling to its capabilities. If you may want to cross compile in the future, please consider one of the next listed models.<br />
* Some distro package managers break Lazarus up in small 'chunks', by default you get the GTK2 chunks. If you plan to use Qt5 also install libqt5pas-dev and lcl-qt5. You can make Qt5 apps using the GTK2 IDE.<br />
* If you plan to work with GTK3, you will need to install libgtk-3-dev, its not declared as a dependency of Lazarus yet because the GTK3 interface is not, yet, complete.<br />
* If you use Debian Bullseye, you should consider getting Lazarus (2.2.0) from Bullseye-Backports, there is an annoying bug in the main repo version that affects IDE rebuilding. Other Debian based distros will probably pick up the Backport or Testing version and they will be fine.<br />
<br />
===Debian-based 64-bit with Lazarus 32-bit===<br />
<br />
On Debian system, like Ubuntu or Mint, run in terminal:<br />
<br />
sudo dpkg --add-architecture i386<br />
sudo apt-get update<br />
<br />
And then install the FPC-Lazarus i386 deb-packages in right order:<br />
<br />
* fpc-laz first<br />
* fpc-src next<br />
* finally lazarus-project <br />
<br />
If you have error message like "cannot find GCC", then run in terminal:<br />
<br />
sudo apt-get install -y gcc:i386<br />
<br />
=== FPC and Lazarus from SourceForge ===<br />
<br />
If your Package Manager cannot offer you the correct version of FPC then probably SourceForge can (The Raspberry Pi being a notable exception) (! But be prepared for your package manager to complain about being bypassed, just insist you know what you are doing !<br />
<br />
All packages can be found at https://sourceforge.net/projects/lazarus/files/ , if you want just FPC, you will find it down in the corresponding Lazarus release directory.<br />
<br />
Navigate down, select the appropriate packages, again, fpc_something, fpc-src_something and lazarus_something, save them locally and install. WARNING, very important, this catches many users out ! Like most Linux packages, these packages have dependencies, you must install using a tool that resolves dependencies. Commands like "dpkg -i fpc_something.deb" will, most definitely leave an incomplete installation ! <br />
<br />
Further, you might need to manually install gdb, the GNU debugger if you are not going to use fpdebug. A debugger is not absolutely essential but it makes life a lot easier. If you plan to work with GTK3, or Qt5 you will need to manually install libgtk-3-dev or libqt5pas-dev. <br />
<br />
'''On a DEB based System.'''<br />
On a older DEB based system, you almost certainly should use gdebi to install any downloaded DEB. It might already be installed, if not, install it first. You can use gdebi at the command line or double click a downloaded package in your file manager. But remember, most important, install first fpc, then fpc-src, then and only then, Lazarus. On more recent deb based systems, the apt command can resolve dependencies, you could then use this command (but note the '''very important ./''' in front of each deb file, if you don't specify the exact location of your downloaded deb file, it will go and get the repository based one) -<br />
<br />
<syntaxhighlight lang="bash">sudo apt install ./fpc_something.deb ./fpc-src_something.deb ./lazarus-something.deb</syntaxhighlight><br />
in mid 2022 for example, you might download from https://sourceforge.net/projects/lazarus/files/Lazarus%20Linux%20amd64%20DEB/Lazarus%202.2.2/ and move the files to a suitable place and then type -<br />
<br />
<syntaxhighlight lang="bash">sudo apt install ./fpc-laz_3.2.2-210709_amd64.deb ./fpc-src_3.2.2-210709_amd64.deb ./lazarus-project_2.2.0-0_amd64.deb<br />
</syntaxhighlight><br />
<br />
'''On an RPM Based System'''<br />
Installing downloaded packages at the command line as shown below. (Author is unaware of the GUI Package tool that does, in fact, resolve dependencies) -<br />
<br />
<syntaxhighlight lang="bash">// RPM Users -<br />
yum localinstall fpc_something.rpm<br />
yum localinstall fpc-src_something.rpm<br />
yum localinstall lazarus-something.rpm<br />
</syntaxhighlight><br />
<br />
Note : later RPM based systems use the '''dnf''' command instead of yum, same syntax.<br />
<br />
=== Build Lazarus from Source ===<br />
<br />
Surprisingly easy, but that is because Lazarus routinely rebuilds itself, eg when a Lazarus package is added. So, its also a useful test that all required dependencies are really present. It allows you to have multiple versions of Lazarus installed ( [[Multiple_Lazarus | see --pcp=xxx]] ) and both Lazarus and LCL are in your own disk space so no write issues. However, you will need to manually add an entry in your OS Menu system and, perhaps distinctive Lazarus icons. <br />
<br />
{{Note| Remember, FPC and Lazarus are two, separate products. Before thinking about building Lazarus, you must have a working and tested FPC. Mid 2023 FPC 3.2.2 was what you probably should be using, many distributions will have it available, otherwise, you need to install it from one of the ways documented on [[ Installing_the_Free_Pascal_Compiler#Linux | Installing the Free Pascal Compiler]] .}}<br />
<br />
{{Note| Dependencies - First and foremost, a suitable version of FPC, see above. You probably want to build the GTK2 version of Lazarus, most distros will have gtk2 preinstalled but you may need to install its dev libraries. So, install libx11-dev and libgtk2.0-dev, that will bring gtk2 itself along if necessary. Possibly (see above) gdb. If building a QT5 version, you will need libqt5pas-dev. If you are going to work with GTK3, you will need to install libgtk-3-dev}}<br />
<br />
{{Note| Systems with limited memory, such as the '''Raspberry Pi''' may need you to increase swap space to at least a Gig before building Lazarus, RasPi page. See [[Lazarus_on_Raspberry_Pi#Correcting_swap_file_size | Setting Raspberry Pi Swap Size]] }}<br />
<br />
The example here pulls down what some consider 'brave', the trunk or main development version. The same process can get you releases, fixes or release candidates. Note this is not a script, it is a series of commands, to be copied one at a time, so you can see whats happening ! <br />
<br />
==== Get the Lazarus Source ====<br />
There are two different ways to download the Lazarus Source (since August 2021) from gitlab, in fact there are several variations of each possible too) -<br />
<br />
'''As a Zip/Tar ball.'''<br />
<br />
If you just want a quick snapshot of trunk, or perhaps want to test the release candidate (and that a great idea) then you may not need access to the developer capabilities that a Git install provides. So, you don't need any git tools on your local machine and its quick and easy. While you can do it through the Gitlab web interface from https://gitlab.com/freepascal.org/lazarus/lazarus its easier to describe with command line tools, start by cd-ing to a suitable place, I use $HOME/bin/Lazarus, then -<br />
<br />
wget https://gitlab.com/freepascal.org/lazarus/lazarus/-/archive/main/lazarus-main.zip<br />
unzip lazarus-main<br />
cd lazarus-main<br />
<br />
'''Some recommended downloads'''<br />
<br />
* Current stable - lazarus_3_0 - https://gitlab.com/freepascal.org/lazarus/lazarus/-/archive/lazarus_3_0/lazarus-lazarus_3_0.zip<br />
<br />
* trunk, or as its now known, main. This is the default from gitlab - https://gitlab.com/freepascal.org/lazarus/lazarus/-/archive/main/lazarus-main.zip<br />
<br />
<br />
<br />
You can see all these options, and lot more on the gitlab site by pulling down the dropbox, left of center, near the top of the page.<br />
<br />
'''As a git (or svn) repository.'''<br />
<br />
On the other hand, if you will be contributing patches, fixes, to Lazarus, or tracking down bugs by bisecting recent releases, then a proper git install is undoubtedly the way forward. You will need git installed locally, again, go to a suitable directory and:<br />
<br />
git clone https://gitlab.com/freepascal.org/lazarus/lazarus.git lazarus-main<br />
cd lazarus-main<br />
<br />
For svn (noting that git might be a better choice) :<br />
<br />
svn checkout --depth files https://github.com/fpc/Lazarus/branches lazarus-all<br />
cd lazarus-all<br />
svn update --set-depth infinity main<br />
<br />
The above svn invocation is more complex due to a bug in many versions of Subversion. <br />
<br />
This is a bit slower process the first time, later you will use git/svn tools to update it and thats a lot faster. As bug reports must be tested against main, it probably does not make sense downloading git versions of anything other than main and maybe fixes. Just pull down a zip or tarball. <br />
<br />
'''Building Lazarus'''<br />
<br />
Remembering that if you have less than 4G ram, you must set 1G or preferably 2G of swap.<br />
<br />
<syntaxhighlight lang="bash"><br />
cd lazarus-main # or, where ever you have put your Lazarus source code<br />
make clean<br />
make bigide<br />
</syntaxhighlight><br />
<br />
You can now start Lazarus with the command ./lazarus or, if you are in another directory, something like -<br />
<br />
<syntaxhighlight lang="bash">/home/[your-user-name]/bin/Lazarus/lazarus_2_2_6/lazarus<br />
</syntaxhighlight><br />
<br />
You can add that directory to your path, put a script in ~/bin to start it or whatever, its Linux, you choose ! Here is an example script to put in your personal bin directory, note it includes FPC, if thats already on your path, leave that line out -<br />
<br />
<syntaxhighlight lang="bash">#!/bin/bash <br />
RELEASE="lazarus-main"<br />
cd "$HOME/bin/Lazarus/""$RELEASE"<br />
PATH="$HOME/bin/FPC/fpc-3.2.2/bin:""$PATH"<br />
./lazarus --pcp="$HOME/.""$RELEASE"</syntaxhighlight><br />
<br />
Out of the box, lazarus stores its config files in $HOME/.lazarus - that might be all you need. But multiple installs require multiple config directories, thats why the --pcp switch on the command line. Alternatively, you can put a lazarus.cfg file in the top level Lazarus directory, where the lazarus binary is containing your chosen command line switch, this is a great way to ensure you don't mix up config data, lazarus does not like that !<br />
<br />
==== Menu items and Icons ====<br />
<br />
Building from source leaves you without the nice main menu entries and pretty icons that people who installed using packages get. But even that is easy to fix. You need two things, a suitable icon and an appropriate desktop file. Here is how -<br />
<br />
First, download a Lazarus icon to use (its just an image, does not need to be from right version of Lazarus), <br />
<br />
<syntaxhighlight lang="bash"><br />
cd ~/.icons; wget https://github.com/fpc/Lazarus/raw/main/images/icons/lazarus256x256.png; cd -<br />
</syntaxhighlight><br />
<br />
Then create a desktop file, eg ~/.local/share/applications/lazarus.desktop that looks a bit like this<br />
<br />
<syntaxhighlight lang=ini><br />
[Desktop Entry]<br />
Name=Lazarus<br />
GenericName=ide<br />
Comment=ide<br />
Exec=/home/myusername/bin/Lazarus/fixes_2_0/lazarus<br />
Icon=/home/myusername/.icons/lazarus256x256.png<br />
Terminal=false<br />
Type=Application<br />
Categories=Utility;Development;<br />
</syntaxhighlight><br />
<br />
You will almost certainly have to change some things and might want to change more -<br />
<br />
* '''Name=''' - This is what will appear in your main menu.<br />
* '''Exec=''' - This is the path to your executable lazarus, in my case I keep my various installs in ~/bin/Lazarus, yours may be different. Its unlikely your user name is 'myusername' so please change it. Note you could add a --pcp command line switch as mentioned above. If you have multiple installs of Lazarus, make multiple desktop files and make sure each uses a separate set of config files. Most important ! You might like to point this item to the little bash script mentioned above, its an easy way of ensuring Lazarus gets the FPC path setting.<br />
* '''Icon=''' The full path to the icon we downloaded earlier. The icon does not need be exactly there, but its a good place.<br />
* '''Categories=''' This line determines which sub-menu Lazarus will show up under. As shown, its under "Programing" on my system, "GNOME;Utility" will pop it up under "Accessories".<br />
<br />
==== Help Files ====<br />
<br />
FPC and Lazarus have quite a wide range of help files and related resources. When building Lazarus from source, you don't get the pre installed help chm that respond, for example, when you press F1. But they are easy to add, download a zip file into, typically, a directory called 'help'. Then, in Lazarus, Tools->Options->Help->Help_Options->CHM_Help_Viewer. Set Help Files Path to the directory you just put all the Chm files into. Get these files from here -<br />
* If using Lazarus Main, https://www.stack.nl/~marcov/doc-chmbeta.zip<br />
* If using a (recent) release, you might be better with https://www.stack.nl/~marcov/doc-chm.zip<br />
These files are very kindly updated from time to time by Marcov. If you prefer, you can build your own in the source tree.<br />
<br />
There are ways to access, possibly a newer version of the same data -<br />
* An on line version - https://lazarus-ccr.sourceforge.io/docs/<br />
* A preview of the next release https://dsiders.gitlab.io/lazdocsnext/ maintained by Don, thanks Don!<br />
<br />
==== What does the <tt>BigIDE</tt> argument to <tt>make</tt> do? ====<br />
<br />
{{BigIDE}}<br />
<br />
=== FPCUPdeluxe ===<br />
<br />
Another approach is [[fpcupdeluxe]], its an application that you run on your computer to manage your FPC / Lazarus install. Its very suited to people who just want to get a working install quickly and are happy to allow fpcupdeluxe to do the thinking for them.<br />
<br />
== See Also ==<br />
=== Other Relevant Pages ===<br />
* [[Installing the Free Pascal Compiler]]<br />
* [[Cross_compiling]]<br />
* [[Cross_Compile_to_RasPi_from_Linux]]<br />
* Generic [[Installing Lazarus]]<br />
* [[Lazarus on Raspberry Pi]]<br />
* Install the help system, [[Installing Help in the IDE]]<br />
* A number of useful additions but in particular Unit/Identifier Dictionary, [[Cody]].<br />
* Some useful tricks associated with the IDE [[IDE tricks]]<br />
<br />
=== Testing FPC installation ===<br />
<br />
See [[Installing_the_Free_Pascal_Compiler#Testing_the_FPC_Install|Testing the FPC Install]] for a very simple test that FPC is working.<br />
<br />
If that worked, well done ! Now proceed to installing Lazarus.<br />
<br />
== Further Reading ==<br />
<br />
Much discussion in a forum thread created by Handoko, https://forum.lazarus.freepascal.org/index.php/topic,41524.0.html<br />
This forum thread may be of particular interest to people who have upgraded an existing Linux install to a more recent version (ie Ubuntu 10.04 to 18.04) as that does seem to trigger some unexpected dependency problems. Please, ask for help, document such problems here when you fix them.<br />
<br />
[[Category:FPC]]<br />
[[Category:Lazarus]]<br />
[[Category:Ubuntu]]<br />
[[Category:Install]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Exceptions&diff=157940Exceptions2023-12-22T03:59:29Z<p>Dbannon: clarified Windows issue with Exceptions. Must use sysutils.</p>
<hr />
<div>{{Exceptions}}<br />
<br />
[[FPC|Free Pascal]] supports exceptions. Exceptions are useful for error handling and avoiding resource leaks. However, bear in mind that exceptions have a performance impact.<br />
<br />
The official documentation is here: [https://www.freepascal.org/docs-html/ref/refch17.html Reference guide chapter 17].<br />
<br />
By default exceptions are disabled. You can opt in by using the [[Mode_ObjFPC|ObjFPC]] or [[Mode_Delphi|DELPHI]] [[Compiler Mode]], or adding -Sx to the [[Compiler|compiler]] [[Command-line_interface|commandline]]. This enables the [[Try|<syntaxhighlight lang="pascal" inline>try</syntaxhighlight>]], [[Raise|<syntaxhighlight lang="pascal" inline>raise</syntaxhighlight>]], [[Except|<syntaxhighlight lang="pascal" inline>except</syntaxhighlight>]], and [[Finally|<syntaxhighlight lang="pascal" inline>finally</syntaxhighlight>]] [[Reserved word|reserved words]] for use in your own code, but it doesn't enable exception generation from the [[RTL]]. To enable the RTL to raise exceptions instead of generating [[runtime error]]s, use the SysUtils unit in your [[Program|program]].<br />
<br />
The base <syntaxhighlight lang="pascal" inline>Exception</syntaxhighlight> [[Class|class]] is found in the [https://www.freepascal.org/docs-html/rtl/sysutils/exception.html SysUtils] unit. All exceptions should preferably use this class or its descendants.<br />
<br />
SysUtils automatically sets up a basic exception catcher, so any otherwise uncaught exception is displayed in human-readable form, as the program terminates. To generate readable callstacks from caught exceptions in your own code, without necessarily terminating the program, you can use SysUtils functions <syntaxhighlight lang="pascal" inline>ExceptAddr</syntaxhighlight>, <syntaxhighlight lang="pascal" inline>ExceptFrames</syntaxhighlight>, <syntaxhighlight lang="pascal" inline>ExceptFrameCount</syntaxhighlight>, and <syntaxhighlight lang="pascal" inline>BackTraceStrFunc</syntaxhighlight>.<br />
<br />
<br />
While it is possible to raise an exception with any class descending from TObject, it is recommended to use Exception as the basis of exception class objects: the Exception class introduces properties to associate a message and a help context with the exception being raised. What is more, the SysUtils unit sets the necessary hooks to catch and display unhandled exceptions: in such cases, the message displayed to the end user, will be the message stored in the exception class.<br />
<br />
On Windows with SEH using try … except-blocks (without using the SysUtils unit) will not behave as expected (from FPC 3.2.0 on). This due to a bug that will, in all probability be fixed at some stage. In the mean time, the very simple solution is to "use SysUtils".<br />
<br />
== Examples ==<br />
<br />
<br />
Note that Pascal uses different [[Keyword|keywords]] than some other languages: <syntaxhighlight lang="pascal" inline>raise</syntaxhighlight> instead of <syntaxhighlight lang="pascal" inline>throw</syntaxhighlight>, and <syntaxhighlight lang="pascal" inline>except</syntaxhighlight> instead of <syntaxhighlight lang="pascal" inline>catch</syntaxhighlight>. Also, as a compiler design choice, each <syntaxhighlight lang="pascal" inline>try</syntaxhighlight>-block can pair with either an <syntaxhighlight lang="pascal" inline>except</syntaxhighlight> or <syntaxhighlight lang="pascal" inline>finally</syntaxhighlight> block, but not both at the same time. You'll need to nest <syntaxhighlight lang="pascal" inline>try</syntaxhighlight>-blocks if you need <syntaxhighlight lang="pascal" inline>except</syntaxhighlight> and <syntaxhighlight lang="pascal" inline>finally</syntaxhighlight> protecting the same code.<br />
<br />
=== Error handling ===<br />
<br />
<syntaxhighlight lang="pascal"><br />
uses sysutils;<br />
<br />
begin<br />
try<br />
// Do something that might go wrong.<br />
except<br />
begin<br />
// Try to recover or show the user an error message.<br />
end;<br />
end;<br />
end.<br />
</syntaxhighlight><br />
<br />
=== Cleaning up resources ===<br />
<br />
<syntaxhighlight lang="pascal"><br />
try<br />
// Do something that might go wrong.<br />
finally<br />
// Clean-up code that is always called even if an exception was raised.<br />
end;<br />
</syntaxhighlight><br />
<br />
=== Exception leaking ===<br />
<br />
Finally-blocks don't destroy exception objects. Any exception that reaches the program's "end." will trigger a memory leak warning. An extra except block can be used to consume such exceptions. This is [https://bugs.freepascal.org/view.php?id=33650 Delphi-compatible].<br />
<br />
<syntaxhighlight lang="pascal"><br />
begin<br />
try<br />
// Your main program, where an exception object is created.<br />
finally<br />
try<br />
// Clean-up code.<br />
except<br />
end;<br />
end;<br />
end.<br />
</syntaxhighlight><br />
<br />
=== Signalling problems ===<br />
<br />
<syntaxhighlight lang="pascal"><br />
raise Exception.Create('Helpful description of what went wrong');<br />
</syntaxhighlight><br />
<br />
=== Using specialised exception types to signal different problems ===<br />
<br />
<syntaxhighlight lang="pascal"><br />
type EMyLittleException = Class(Exception);<br />
<br />
begin<br />
try<br />
raise EMyLittleException.Create('Foo');<br />
except<br />
on E : EMyLittleException do writeln(E.Message);<br />
on E : Exception do writeln('This is not my exception!');<br />
else writeln('This is not an Exception-descendant at all!');<br />
end;<br />
end;<br />
</syntaxhighlight><br />
<br />
=== Re-raising exceptions ===<br />
<br />
<syntaxhighlight lang="pascal"><br />
try<br />
// Do something that might go wrong.<br />
except<br />
// Try to recover or show the user an error message.<br />
if recoveredSuccessfully = FALSE then<br />
raise;<br />
end;<br />
</syntaxhighlight><br />
<br />
<br />
== Exception classes ==<br />
<br />
<br />
SysUtils defines and raises many specific [https://www.freepascal.org/docs-html/rtl/sysutils/index-4.html exception classes].<br />
<br />
With SysUtils included in your program, and exceptions enabled, various [https://www.freepascal.org/docs-html/user/userap4.html runtime errors] are changed into exceptions. Processor-level interrupts like SIGSEGV or SIGFPU, which normally translate to run-time errors, are also changed to exceptions. For example, run-time error 200 (division by zero) becomes EDivByZero or EZeroDivide, while run-time error 216 (a general protection fault) becomes EAccessViolation.<br />
<br />
<br />
== Best practice ==<br />
<br />
<br />
* Raise exceptions to signal that an operation could not be completed, where it normally should have succeeded.<br />
* Do not use exceptions as part of expected control flow. Instead, add checks for common error conditions and return error values the old-fashioned way. For example, input parsing problems or file existence fails are usually not truly exceptional.<br />
* But do raise an exception if it's critical that the error is noticed; programmers may forget to check for returned error values.<br />
* Naming convention: Prefix exception class names with a capital E.<br />
* Re-raise exceptions in <syntaxhighlight lang="pascal" inline>except</syntaxhighlight>-blocks using <syntaxhighlight lang="pascal" inline>raise;</syntaxhighlight> if you were unable to recover from the problem; this preserves the original exception's callstack.<br />
* Be careful about using the catch-all base <syntaxhighlight lang="pascal" inline>Exception</syntaxhighlight> class, since the underlying code or OS/filesystem might produce an unanticipated exception that slips through your exception handler, potentially corrupting the program state.<br />
* When writing a unit or library, if exceptions are used at all, they should be documented clearly up front. This way the unit user knows what to expect.<br />
* Keep exception handling away from code that needs to run as fast as possible.<br />
<br />
<br />
== Performance ==<br />
<br />
<br />
Compiler optimisation levels don't make much difference. All exception blocks use a small amount of wrapper code that can't be optimised away. There are different ways for a compiler to produce exception code, with varying performance implications. As of FPC 3.0.4, the default exception mechanism uses the standard setjmp/longjmp style. FPC also supports OS-provided Structured Exception Handling; this is the default on Win64, and can be enabled on Win32 (recompile the compiler with <syntaxhighlight lang="pascal" inline>$define TEST_WIN32_SEH</syntaxhighlight>). Other mechanisms may be added to FPC in the future.<br />
<br />
To get a better feel for what's going on, try writing a small test program and compiling it with the <syntaxhighlight lang="pascal" inline>-a</syntaxhighlight> switch. This leaves behind a human-readable assembly file.<br />
<br />
Notes on the setjmp/longjmp method:<br />
* <syntaxhighlight lang="pascal" inline>Try</syntaxhighlight>-statements insert some extra address calculations, a stack push and pop, some direct jumps, and a conditional jump. This is the setup cost of an exception frame, even if no exception is raised.<br />
* <syntaxhighlight lang="pascal" inline>Except</syntaxhighlight>-statements insert the above, plus a push and pop, more direct jumps, and another conditional jump.<br />
* <syntaxhighlight lang="pascal" inline>Finally</syntaxhighlight>-statements add a pop and a conditional jump.<br />
* <syntaxhighlight lang="pascal" inline>Raise</syntaxhighlight>-statements create a string and pass control to FPC's exception raiser. The string creation itself can spawn an implicit exception frame for the function/method, due to dynamic string allocations, even if the <syntaxhighlight lang="pascal" inline>raise</syntaxhighlight> is not executed. This is why in e.g. container types one often sees the <syntaxhighlight lang="pascal" inline>raise</syntaxhighlight> moved to a separate local (private) procedure. This localises the exception frame to that procedure, so the performance hit is only taken if the procedure is called.<br />
<br />
Furthermore, generating a human-readable callstack from a raised exception involves time-consuming stack traversal and string manipulation.<br />
<br />
With all that said, outside of heavy processing code, the convenience of exceptions usually outweighs their performance impact. Don't feel bad about using them if it makes your code better.<br />
<br />
== Further reading ==<br />
<br />
[[Logging exceptions]]<br />
<br />
[[Avoiding implicit try finally section]]<br />
<br />
[http://codenewsfast.com/cnf/article/0/permalink.art-ng53q201750 Exceptions vs Error codes] - a situation where exceptions might be a danger in some code (see middle of this page)</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Lazarus_on_Raspberry_Pi&diff=157939Lazarus on Raspberry Pi2023-12-22T03:53:20Z<p>Dbannon: moving swap file</p>
<hr />
<div>{{Lazarus_on_Raspberry_Pi}}<br />
<br />
[[File:Lazarus on Raspberry Pi Raspian Wheezy version 2012-10-28.png|thumb|200px|right|alt=Lazarus on Raspbian Wheezy.|Lazarus on Raspbian Wheezy]]<br />
<br />
{{Platform only|Raspberry Pi}}<br />
<br />
The '''Raspberry Pi''' is a credit-card-sized single-board computer. It has been developed in the UK by the Raspberry Pi Foundation with the intention of stimulating the teaching of basic computer science in schools. Raspberry Pis are also used for multiple other purposes that are as different as media servers, robotics and control engineering.<br />
<br />
The Raspberry Pi Foundation recommends [[Raspbian | the Pasperry Pi OS]] as standard operating system. Alternative systems running on RPI include RISC OS and various [[Portal:Linux|Linux]] distributions, as well as [[Portal:Android|Android]] and [[Portal:FreeBSD|FreeBSD]].<br />
<br />
Lazarus runs natively under the Raspbian operating system.<br />
<br />
==Installing and compiling Lazarus==<br />
<br />
=== Introduction ===<br />
Installing FPC and Lazarus on a Raspberry Pi is very little different from other Linux boxes, so please refer to [[Installing_Lazarus_on_Linux]]. <br />
How ever, there are some RasPi particularities worth noting.<br />
* RasPi do not always have enough memory to build Lazarus, this affects the routine rebuild of the IDE that happens when installing packages for example. The solution is to increase swap size (below).<br />
* The Lazarus SourceForge repository does not include a pre-made RasPi package. <br />
* If you are using the Raspberry Pi OS, its based on Debian and Debian has quite a long release cycle. So, at any one time, the repo provided Lazarus is likely to be out of date and, occasionally, so will the FPC be.<br />
* Notwithstanding the above point, at present, late 2023, the Debian Bookworm based Raspberry Pi OS has the appropriate FPC and a Lazarus that suits most people. This will change !<br />
<br />
=== Correcting swap file size ===<br />
(Info from forum user "Thaddy", updated.)<br />
If you have RPi with memory size less then 4Gb, and you want to build Lazarus from source or use FPCUPdeluxe or just rebuild the IDE after installing a package you need to allocate at least 1G if not 2G of swap.<br />
This appears to be particularly so with Raspberry Pi OS based on Bookworm rather than Bullseye. <br />
<br />
To increase swap you should firstly ensure you have a couple of gig free and -<br />
<br />
* sudo dphys-swapfile swapoff<br />
* sudo nano /etc/dphys-swapfile<br />
* in the file, find CONF_SWAPSIZE and change the value to 2048 (or 1024) and save it.<br />
* sudo dphys-swapfile setup<br />
* sudo dphys-swapfile swapon<br />
<br />
You should now see the file, /var/swap, increase to 2G (or 1G). On some other operating systems, eg a "pure" debian, you may need to install the dphys-swapfile package. <br />
<br />
If you don't have enough memory (real memory and swap) then, your compile processes (particularly assembling) will stop and the whole machine freezes. You have been warned !<br />
<br />
While you are fiddling with the swap file, consider, if possible, moving the swap file from /var/swap to somewhere off the SDCard, unfortunately, SDCards are not good at the repetitive writes that happen in a swap file. Not critical but a good idea.<br />
<br />
===Simple installation ===<br />
<br />
Most distributions have a graphic installer of some sort, look for entries like "Add / Remove Software" in your menus. Alternatively, all systems will have a command line interface. As hinted above, its likely that your distribution will have a current FPC, if so, best to use it. More interesting is Lazarus, as it changes more frequently, consider your needs. The Raspberry Pi OS, late 2023, has Lazarus 2.2.6 and that is the the latest release. While version 3.0 of Lazarus is not too far away, most users will, for the time being, be quite happy with Lazarus 2.2.6. Later on, you can remove the distro supplied Lazarus 2.2.6 and build 3.0 yourself, its easy.<br />
<br />
A advantage of using the distro supplied FPC and Lazarus is that they test them, make sure everything needed is there and its very easy. <br />
<br />
See [https://wiki.freepascal.org/Installing_Lazarus_on_Linux#The_Package_Manager_Model the Package Manager Model].<br />
<br />
=== Not Quite as Simple Install ===<br />
<br />
There are two other functional alternatives for installing Lazarus, building from source and using [[fpcupdeluxe]], an application that you run on your computer to manage your FPC / Lazarus install. Its very suited to people who just want to get a working install quickly, fpcupdeluxe will do the thinking for them. Fpcupdelux will install (and manage) both FPC and Lazarus and you should use them together.<br />
<br />
Installing a non distro FPC is a touch more complicated still. Its [https://wiki.freepascal.org/Installing_the_Free_Pascal_Compiler#Compiling_the_FPC_source.2C_using_preinstalled_FPC possible] to build a new FPC using an older, existing, FPC, might be a challenge for a less powerful Pi. An better option is to [https://wiki.freepascal.org/Installing_the_Free_Pascal_Compiler#Downloading_FPC_tar_balls download and install] a tarball. <br />
<br />
Its quite reasonable to use the distro provided FPC and build your own Lazarus. In fact, many users have several lazarus versions installed this way. <br />
<br />
Building Lazarus [https://wiki.freepascal.org/Installing_Lazarus_on_Linux#Build_Lazarus_from_Source from source] on a Raspberry Pi is no different from other Linuxes, except, perhaps, for the [[Lazarus_on_Raspberry_Pi#Correcting_swap_file_size | memory issue]].<br />
<br />
<gallery><br />
File:Lazarus 0 9 30 4 on RPi 2.png|Lazarus "out of the box" on Raspberry Pi 2<br />
</gallery><br />
<br />
===Cross compiling for the Raspberry Pi from Windows===<br />
1. Using fpcup<br />
<br />
One way is to use fpcup to set up a cross compiler; follow these instructions:<br />
[[fpcup#Linux_ARM_cross_compiler]]<br />
<br />
2. Using scripts<br />
<br />
Alternatively, for a more manual approach using batch files, you can follow these steps.<br />
<br />
2.1 Prerequisites<br />
FPC 2.7.1 or higher installed with source code<br />
Install the Windows version from the Linaro binutils for linux gnueabihf into %FPCPATH%/bin/win32-armhf-linux [https://launchpad.net/linaro-toolchain-binaries/trunk/2013.10/+download/gcc-linaro-arm-linux-gnueabihf-4.8-2013.10_win32.zip]<br />
<br />
2.2 Example Windows Batch file (adapt paths as needed)<br />
<br />
<syntaxhighlight lang="bat"><br />
set PATH=C:\pp\bin\i386-win32;%PATH%;<br />
set FPCMAKEPATH=C:/pp<br />
set FPCPATH=C:/pp<br />
set OUTPATH=C:/pp271<br />
%FPCMAKEPATH%/bin/i386-win32/make distclean OS_TARGET=linux CPU_TARGET=arm CROSSBINDIR=%FPCPATH%/bin/win32-armhf-linux CROSSOPT="-CpARMV6 -CfVFPV2 -OoFASTMATH" FPC=%FPCPATH%/bin/i386-win32/ppc386.exe<br />
<br />
%FPCMAKEPATH%/bin/i386-win32/make all OS_TARGET=linux CPU_TARGET=arm CROSSBINDIR=%FPCPATH%/bin/win32-armhf-linux CROSSOPT="-CpARMV6 -CfVFPV2 -OoFASTMATH" FPC=%FPCPATH%/bin/i386-win32/ppc386.exe<br />
if errorlevel 1 goto quit<br />
%FPCMAKEPATH%/bin/i386-win32/make crossinstall CROSSBINDIR=%FPCPATH%/bin/win32-armhf-linux CROSSOPT="-CpARMV6 -CfVFPV2 -OoFASTMATH" OS_TARGET=linux CPU_TARGET=arm FPC=%FPCPATH%/bin/i386-win32/ppc386.exe INSTALL_BASEDIR=%OUTPATH%<br />
<br />
:quit<br />
pause<br />
</syntaxhighlight><br />
<br />
With the resulting ppcrossarm.exe and ARM RTL you will be able to build a cross Lazarus version as usual and compile FPC projects for the Raspberry Pi and other armhf devices.<br />
<br />
Remember that not all - especially Windows - libraries are available for Linux arm.<br />
<br />
===Cross compiling for the Raspberry Pi from Linux===<br />
<br />
Please see this page https://wiki.freepascal.org/Cross_Compile_to_RasPi_from_Linux<br />
<br />
==Accessing external hardware==<br />
[[File:rpi pinout all 50.png|thumb|300px|right|alt=Raspberry Pi pinout|Raspberry Pi pinout of external connectors]]<br />
<br />
One of the goals in the development of Raspberry Pi was to facilitate effortless access to external devices like sensors and actuators. There are many ways to access the I/O facilities from Lazarus and Free Pascal:<br />
# [[Lazarus on Raspberry Pi# Native hardware access|Direct access]] using the [[BaseUnix]] unit<br />
# Access through [[Lazarus on Raspberry Pi# Hardware access via encapsulated shell calls|encapsulated shell calls]]<br />
# Access through the [[Lazarus on Raspberry Pi# wiringPi procedures and functions|wiringPi library]].<br />
# Access through Unit [[Lazarus on Raspberry Pi# rpi_hal-Hardware Abstraction Library (GPIO, I2C und SPI functions and procedures)|rpi_hal]].<br />
# Access through Unit [[Lazarus on Raspberry Pi# PiGpio Low-level native pascal unit (GPIO control instead of wiringPi c library)|PiGpio]].<br />
# Access through the [https://github.com/SAmeis/pascalio PascalIO] library.<br />
# Access through the [http://asphyre.net/products/pxl PXL] library.<br />
<br />
=== Native hardware access===<br />
[[File:testprogram for GPIO on RPI annotated.png|Simple test program for accessing the GPIO port on Raspberry Pi|thumb|300px|right]]<br />
[[File:rpi testcircuit 1d.png|Test circuit for GPIO access with the described program|thumb|300px|right]]<br />
[[File:rpi testcircuit 1p annotaded.png|Simple demo implementation of the circuit from above on a breadboard|thumb|300px|right]]<br />
This method provides access to external hardware that doesn't require additional libraries. The only requirement is the BaseUnix library that is part of Free Pascal's RTL.<br />
<br />
==== Switching a device via the GPIO port ====<br />
The following example lists a simple program that controls the GPIO pin 17 as output to switch an LED, transistor or relays. This program contains a [[doc:lcl/stdctrls/ttogglebox.html|ToggleBox]] with name ''GPIO17ToggleBox'' and for logging return codes a [[doc:lcl/stdctrls/tmemo.html|TMemo]] called ''LogMemo''.<br />
<br />
For the example, the anode of a LED has been connected with Pin 11 on the Pi's connector (corresponding to GPIO pin 17 of the BCM2835 SOC) and the LED's cathode was wired via a 68 Ohm resistor to pin 6 of the connector (GND) as previously described by Upton and Halfacree. Subsequently, the LED may be switched on and off with the application's toggle box.<br />
<br />
The code may at first require to be run as root, i.e. either from a root account (not recommended) or via '''su'''.<br />
A better option is to add the user to the gpio group, the i2c group and the spi group.<br />
<br />
<syntaxhighlight lang="bash"><br />
sudo adduser pi gpio<br />
sudo adduser pi i2c<br />
sudo adduser pi spi<br />
</syntaxhighlight><br />
<br />
''Controlling unit:''<br />
<syntaxhighlight lang="pascal"><br />
unit Unit1;<br />
<br />
{Demo application for GPIO on Raspberry Pi}<br />
{Inspired by the Python input/output demo application by Gareth Halfacree}<br />
{written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5}<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
Unix, BaseUnix;<br />
<br />
type<br />
<br />
{ TForm1 }<br />
<br />
TForm1 = class(TForm)<br />
LogMemo: TMemo;<br />
GPIO17ToggleBox: TToggleBox;<br />
procedure FormActivate(Sender: TObject);<br />
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
procedure GPIO17ToggleBoxChange(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
const<br />
PIN_17: PChar = '17';<br />
PIN_ON: PChar = '1';<br />
PIN_OFF: PChar = '0';<br />
OUT_DIRECTION: PChar = 'out';<br />
<br />
var<br />
Form1: TForm1;<br />
gReturnCode: longint; {stores the result of the IO operation}<br />
<br />
implementation<br />
<br />
{$R *.lfm}<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.FormActivate(Sender: TObject);<br />
var<br />
fileDesc: integer;<br />
begin<br />
{ Prepare SoC pin 17 (pin 11 on GPIO port) for access: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/export', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, PIN_17[0], 2);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
{ Set SoC pin 17 as output: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/gpio17/direction', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, OUT_DIRECTION[0], 3);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end;<br />
<br />
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
var<br />
fileDesc: integer;<br />
begin<br />
{ Free SoC pin 17: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/unexport', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, PIN_17[0], 2);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end;<br />
<br />
procedure TForm1.GPIO17ToggleBoxChange(Sender: TObject);<br />
var<br />
fileDesc: integer;<br />
begin<br />
if GPIO17ToggleBox.Checked then<br />
begin<br />
{ Swith SoC pin 17 on: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/gpio17/value', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, PIN_ON[0], 1);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end<br />
else<br />
begin<br />
{ Switch SoC pin 17 off: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/gpio17/value', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, PIN_OFF[0], 1);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
''Main program:''<br />
<syntaxhighlight lang="pascal"><br />
program io_test;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
uses<br />
{$IFDEF UNIX}{$IFDEF UseCThreads}<br />
cthreads,<br />
{$ENDIF}{$ENDIF}<br />
Interfaces, // this includes the LCL widgetset<br />
Forms, Unit1<br />
{ you can add units after this };<br />
<br />
{$R *.res}<br />
<br />
begin<br />
Application.Initialize;<br />
Application.CreateForm(TForm1, Form1);<br />
Application.Run;<br />
end.<br />
</syntaxhighlight><br />
<br />
==== Reading the status of a pin ====<br />
[[File:2013-04-28-193243 1280x1024 scrot.png|Demo program for reading the status of a GPIO pin|thumb|300px|right]]<br />
[[File:rpi testcircuit 2d.png|Test circuit for GPIO access with the described program|thumb|300px|right]]<br />
[[File:rpi testcircuit 2p annotaded.png|Possible implementation of this test circuit|thumb|300px|right]]<br />
<br />
Of course it is also possible to read the status of e.g. a switch that is connected to the GPIO port.<br />
<br />
The following simple example is very similar to the previous one. It controls the GPIO pin 18 as input for a binary device like a switch, transistor or relais. This program contains a [[doc:lcl/stdctrls/tcheckbox.html|CheckBox]] with name ''GPIO18CheckBox'' and for logging return codes a [[doc:lcl/stdctrls/tmemo.html|TMemo]] called ''LogMemo''.<br />
<br />
For the example, one pole of a push-button has been connected to Pin 12 on the Pi's connector (corresponding to GPIO pin 18 of the BCM2835 SOC) and via a 10k Ohm pull-up resistor with pin 1 (+3.3V, see wiring diagram). The other pole has been wired to pin 6 of the connector (GND). The program senses the status of the button and correspondingly switches the checkbox on or off, respectively.<br />
<br />
Note that the potential of pin 18 is high if the button is released (by virtue of the connection to pin 1 via the pull-up resistor) and low if it is pressed (since in this situation pin 18 is connected to GND via the switch). Therefore, the GPIO pin signals 0 if the button is pressed and 1 if it is released.<br />
<br />
This program has again to be executed as root.<br />
<br />
''Controlling unit:''<br />
<syntaxhighlight lang="pascal"><br />
unit Unit1;<br />
<br />
{Demo application for GPIO on Raspberry Pi}<br />
{Inspired by the Python input/output demo application by Gareth Halfacree}<br />
{written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5}<br />
<br />
{This application reads the status of a push-button}<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
ButtonPanel, Unix, BaseUnix;<br />
<br />
type<br />
<br />
{ TForm1 }<br />
<br />
TForm1 = class(TForm)<br />
ApplicationProperties1: TApplicationProperties;<br />
GPIO18CheckBox: TCheckBox;<br />
LogMemo: TMemo;<br />
procedure ApplicationProperties1Idle(Sender: TObject; var Done: Boolean);<br />
procedure FormActivate(Sender: TObject);<br />
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
const<br />
PIN_18: PChar = '18';<br />
IN_DIRECTION: PChar = 'in';<br />
<br />
var<br />
Form1: TForm1;<br />
gReturnCode: longint; {stores the result of the IO operation}<br />
<br />
implementation<br />
<br />
{$R *.lfm}<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.FormActivate(Sender: TObject);<br />
var<br />
fileDesc: integer;<br />
begin<br />
{ Prepare SoC pin 18 (pin 12 on GPIO port) for access: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/export', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, PIN_18[0], 2);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
{ Set SoC pin 18 as input: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/gpio18/direction', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, IN_DIRECTION[0], 2);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end;<br />
<br />
procedure TForm1.ApplicationProperties1Idle(Sender: TObject; var Done: Boolean);<br />
var<br />
fileDesc: integer;<br />
buttonStatus: string[1] = '1';<br />
begin<br />
try<br />
{ Open SoC pin 18 (pin 12 on GPIO port) in read-only mode: }<br />
fileDesc := fpopen('/sys/class/gpio/gpio18/value', O_RdOnly);<br />
if fileDesc > 0 then<br />
begin<br />
{ Read status of this pin (0: button pressed, 1: button released): }<br />
gReturnCode := fpread(fileDesc, buttonStatus[1], 1);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode) + ': ' + buttonStatus);<br />
LogMemo.SelStart := Length(LogMemo.Lines.Text) - 1;<br />
if buttonStatus = '0' then<br />
GPIO18CheckBox.Checked := true<br />
else<br />
GPIO18CheckBox.Checked := false;<br />
end;<br />
finally<br />
{ Close SoC pin 18 (pin 12 on GPIO port) }<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
LogMemo.SelStart := Length(LogMemo.Lines.Text) - 1;<br />
end;<br />
end;<br />
<br />
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
var<br />
fileDesc: integer;<br />
begin<br />
{ Free SoC pin 18: }<br />
try<br />
fileDesc := fpopen('/sys/class/gpio/unexport', O_WrOnly);<br />
gReturnCode := fpwrite(fileDesc, PIN_18[0], 2);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
finally<br />
gReturnCode := fpclose(fileDesc);<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
The main program is identical to that of the example from above.<br />
<br />
=== Hardware access via encapsulated shell calls===<br />
Another way to access the hardware is by encapsulating terminal commands. This is achieved by using the [[Executing External Programs#Unix fpsystem, fpexecve and shell|fpsystem]] function. This method gives access to functions that are not supported by the BaseUnix unit. The following code implements a program that has the same functionality as the program resulting from the first listing above.<br />
<br />
''Controlling unit:''<br />
<syntaxhighlight lang="pascal"><br />
unit Unit1;<br />
<br />
{Demo application for GPIO on Raspberry Pi}<br />
{Inspired by the Python input/output demo application by Gareth Halfacree}<br />
{written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5}<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, Unix;<br />
<br />
type<br />
<br />
{ TForm1 }<br />
<br />
TForm1 = class(TForm)<br />
LogMemo: TMemo;<br />
GPIO17ToggleBox: TToggleBox;<br />
procedure FormActivate(Sender: TObject);<br />
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
procedure GPIO17ToggleBoxChange(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
var<br />
Form1: TForm1;<br />
gReturnCode: longint; {stores the result of the IO operation}<br />
<br />
implementation<br />
<br />
{$R *.lfm}<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.FormActivate(Sender: TObject);<br />
begin<br />
{ Prepare SoC pin 17 (pin 11 on GPIO port) for access: }<br />
gReturnCode := fpsystem('echo "17" > /sys/class/gpio/export');<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
{ Set SoC pin 17 as output: }<br />
gReturnCode := fpsystem('echo "out" > /sys/class/gpio/gpio17/direction');<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
<br />
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
begin<br />
{ Free SoC pin 17: }<br />
gReturnCode := fpsystem('echo "17" > /sys/class/gpio/unexport');<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
<br />
procedure TForm1.GPIO17ToggleBoxChange(Sender: TObject);<br />
begin<br />
if GPIO17ToggleBox.Checked then<br />
begin<br />
{ Swith SoC pin 17 on: }<br />
gReturnCode := fpsystem('echo "1" > /sys/class/gpio/gpio17/value');<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end<br />
else<br />
begin<br />
{ Switch SoC pin 17 off: }<br />
gReturnCode := fpsystem('echo "0" > /sys/class/gpio/gpio17/value');<br />
LogMemo.Lines.Add(IntToStr(gReturnCode));<br />
end;<br />
end;<br />
<br />
end.<br />
</syntaxhighlight><br />
<br />
The main program is identical to that of the example above. This program has to be executed with root privileges, too.<br />
<br />
=== wiringPi procedures and functions===<br />
<br />
Alex Schaller's wrapper unit for Gordon Henderson's Arduino compatible wiringPi library provides a numbering scheme that resembles that of Arduino boards.<br />
<br />
'''Function wiringPiSetup:longint:''' Initializes wiringPi system using the wiringPi pin numbering scheme.<br />
<br />
'''Procedure wiringPiGpioMode(mode:longint):''' Initializes wiringPi system with the Broadcom GPIO pin numbering scheme.<br />
<br />
'''Procedure pullUpDnControl(pin:longint;''' pud:longint): controls the internal pull-up/down resistors on a GPIO pin.<br />
<br />
'''Procedure pinMode(pin:longint; mode:longint):''' sets the mode of a pin to either INPUT, OUTPUT, or PWM_OUTPUT.<br />
<br />
'''Procedure digitalWrite(pin:longint; value:longint):''' sets an output bit.<br />
<br />
'''Procedure pwmWrite(pin:longint; value:longint):''' sets an output PWM value between 0 and 1024.<br />
<br />
'''Function digitalRead(pin:longint):longint:''' reads the value of a given Pin, returning 1 or 0.<br />
<br />
'''Procedure delay(howLong:dword):''' waits for at least howLong milliseconds.<br />
<br />
'''Procedure delayMicroseconds(howLong:dword):''' waits for at least howLong microseconds.<br />
<br />
'''Function millis:dword:''' returns the number of milliseconds since the program called one of the wiringPiSetup functions.<br />
<br />
=== rpi_hal-Hardware Abstraction Library (GPIO, I2C and SPI functions and procedures)===<br />
<br />
This Unit with around 1700 Lines of Code provided by Stefan Fischer, delivers procedures and functions to access the rpi HW I2C, SPI and GPIO:<br />
<br />
Just an excerpt of the available functions and procedures:<br />
<br />
'''procedure gpio_set_pin (pin:longword;highlevel:boolean);''' { Set RPi GPIO pin to high or low level; Speed @ 700MHz -> 0.65MHz }<br />
<br />
'''function gpio_get_PIN (pin:longword):boolean;''' { Get RPi GPIO pin Level is true when Pin level is '1'; false when '0'; Speed @ 700MHz -> 1.17MHz } <br />
<br />
'''procedure gpio_set_input (pin:longword);''' { Set RPi GPIO pin to input direction }<br />
<br />
'''procedure gpio_set_output(pin:longword);''' { Set RPi GPIO pin to output direction }<br />
<br />
'''procedure gpio_set_alt (pin,altfunc:longword);''' { Set RPi GPIO pin to alternate function nr. 0..5 }<br />
<br />
'''procedure gpio_set_gppud (mask:longword);''' { set RPi GPIO Pull-up/down Register (GPPUD) with mask }<br />
<br />
...<br />
<br />
'''function rpi_snr :string;''' { delivers SNR: 0000000012345678 }<br />
<br />
'''function rpi_hw :string;''' { delivers Processor Type: BCM2708 }<br />
<br />
'''function rpi_proc:string;''' { ARMv6-compatible processor rev 7 (v6l) }<br />
<br />
...<br />
<br />
'''function i2c_bus_write(baseadr,reg:word; var data:databuf_t; lgt:byte; testnr:integer) : integer;''' <br />
<br />
'''function i2c_bus_read (baseadr,reg:word; var data:databuf_t; lgt:byte; testnr:integer) : integer;'''<br />
<br />
'''function i2c_string_read(baseadr,reg:word; var data:databuf_t; lgt:byte; testnr:integer) : string;''' <br />
<br />
'''function i2c_string_write(baseadr,reg:word; s:string; testnr:integer) : integer;'''<br />
<br />
...<br />
<br />
'''procedure SPI_Write(devnum:byte; reg,data:word);'''<br />
<br />
'''function SPI_Read(devnum:byte; reg:word) : byte;'''<br />
<br />
'''procedure SPI_BurstRead2Buffer (devnum,start_reg:byte; xferlen:longword);'''<br />
<br />
'''procedure SPI_BurstWriteBuffer (devnum,start_reg:byte; xferlen:longword);''' { Write 'len' Bytes from Buffer SPI Dev startig at address 'reg' }<br />
<br />
... <br />
<br />
<br />
''Test Program (testrpi.pas):''<br />
<syntaxhighlight lang="pascal"><br />
//Simple Test program, which is using rpi_hal;<br />
<br />
program testrpi;<br />
uses rpi_hal;<br />
begin<br />
writeln('Show CPU-Info, RPI-HW-Info and Registers:');<br />
rpi_show_all_info;<br />
writeln('Let Status LED Blink. Using GPIO functions:');<br />
GPIO_PIN_TOGGLE_TEST;<br />
writeln('Test SPI Read function. (piggy back board with installed RFM22B Module is required!)');<br />
Test_SPI;<br />
end.<br />
<br />
</syntaxhighlight><br />
<br />
=== PiGpio Low-level native pascal unit (GPIO control instead of wiringPi c library)===<br />
<br />
''This Unit (pigpio.pas[https://docs.google.com/file/d/0B-b-5ooIWPvGN1BIcEJzRDgyeUE/edit?usp=sharing]) with 270 Lines of Code provided by Gabor Szollosi, works very fast (for ex. GPIO pin output switching frequency 8 MHz) :''<br />
<syntaxhighlight lang="pascal"><br />
<br />
unit PiGpio;<br />
{<br />
BCM2835 GPIO Registry Driver, also can use to manipulate cpu other registry areas<br />
<br />
This code is tested only Broadcom bcm2835 cpu, different arm cpus may need different<br />
gpio driver implementation<br />
<br />
2013 Gabor Szollosi<br />
}<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils;<br />
<br />
const<br />
REG_GPIO = $3F000;// bcm2835 gpio register 0x2000 0000 (RPi1), 0x3F00 0000 (RPi2)<br />
// new fpMap uses page offset, one page is 4096 bytes = 0x1000 so simply calculate 0x2000 0000 / 0x1000 = 0x2000 0<br />
PAGE_SIZE = 4096;<br />
BLOCK_SIZE = 4096;<br />
// The BCM2835 has 54 GPIO pins.<br />
// BCM2835 data sheet, Page 90 onwards.<br />
// There are 6 control registers, each control the functions of a block<br />
// of 10 pins.<br />
<br />
CLOCK_BASE = (REG_GPIO + $101);<br />
GPIO_BASE = (REG_GPIO + $200);<br />
GPIO_PWM = (REG_GPIO + $20C);<br />
<br />
INPUT = 0;<br />
OUTPUT = 1;<br />
PWM_OUTPUT = 2;<br />
LOW = False;<br />
HIGH = True;<br />
PUD_OFF = 0;<br />
PUD_DOWN = 1;<br />
PUD_UP = 2;<br />
<br />
// PWM<br />
<br />
PWM_CONTROL = 0;<br />
PWM_STATUS = 4;<br />
PWM0_RANGE = 16;<br />
PWM0_DATA = 20;<br />
PWM1_RANGE = 32;<br />
PWM1_DATA = 36;<br />
<br />
PWMCLK_CNTL = 160;<br />
PWMCLK_DIV = 164;<br />
<br />
PWM1_MS_MODE = $8000; // Run in MS mode<br />
PWM1_USEFIFO = $2000; // Data from FIFO<br />
PWM1_REVPOLAR = $1000; // Reverse polarity<br />
PWM1_OFFSTATE = $0800; // Ouput Off state<br />
PWM1_REPEATFF = $0400; // Repeat last value if FIFO empty<br />
PWM1_SERIAL = $0200; // Run in serial mode<br />
PWM1_ENABLE = $0100; // Channel Enable<br />
<br />
PWM0_MS_MODE = $0080; // Run in MS mode<br />
PWM0_USEFIFO = $0020; // Data from FIFO<br />
PWM0_REVPOLAR = $0010; // Reverse polarity<br />
PWM0_OFFSTATE = $0008; // Ouput Off state<br />
PWM0_REPEATFF = $0004; // Repeat last value if FIFO empty<br />
PWM0_SERIAL = $0002; // Run in serial mode<br />
PWM0_ENABLE = $0001; // Channel Enable<br />
<br />
<br />
type<br />
<br />
{ TIoPort }<br />
<br />
TIoPort = class // IO bank object<br />
private //<br />
<br />
//function get_pinDirection(aPin: TGpIoPin): TGpioPinConf;<br />
<br />
public<br />
FGpio: ^LongWord;<br />
FClk: ^LongWord;<br />
FPwm: ^LongWord;<br />
procedure SetPinMode(gpin, mode: byte);<br />
function GetBit(gpin : byte):boolean;inline; // gets pin bit}<br />
procedure ClearBit(gpin : byte);inline;// write pin to 0<br />
procedure SetBit(gpin : byte);inline;// write pin to 1<br />
procedure SetPullMode(gpin, mode: byte);<br />
procedure PwmWrite(gpin : byte; value : LongWord);inline;// write pin to pwm value<br />
end;<br />
<br />
{ TIoDriver }<br />
<br />
TIoDriver = class<br />
private<br />
<br />
public<br />
destructor Destroy;override;<br />
function MapIo:boolean;// creates io memory mapping<br />
procedure UnmapIoRegisrty(FMap: TIoPort);// close io memory mapping<br />
function CreatePort(PortGpio, PortClk, PortPwm: LongWord):TIoPort; // create new IO port<br />
end;<br />
<br />
var<br />
<br />
fd: integer;// /dev/mem file handle<br />
procedure delayNanoseconds (howLong : LongWord);<br />
<br />
<br />
implementation<br />
<br />
uses<br />
baseUnix, Unix;<br />
<br />
procedure delayNanoseconds (howLong : LongWord);<br />
var<br />
sleeper, dummy : timespec;<br />
begin<br />
sleeper.tv_sec := 0 ;<br />
sleeper.tv_nsec := howLong ;<br />
fpnanosleep (@sleeper,@dummy) ;<br />
end;<br />
{ TIoDriver }<br />
//*******************************************************************************<br />
destructor TIoDriver.Destroy;<br />
begin<br />
inherited Destroy;<br />
end;<br />
//*******************************************************************************<br />
function TIoDriver.MapIo: boolean;<br />
begin<br />
Result := True;<br />
fd := fpopen('/dev/mem', O_RdWr or O_Sync); // Open the master /dev/memory device<br />
if fd < 0 then<br />
begin<br />
Result := False; // unsuccessful memory mapping<br />
end;<br />
//<br />
end;<br />
//*******************************************************************************<br />
procedure TIoDriver.UnmapIoRegisrty(FMap:TIoPort);<br />
begin<br />
if FMap.FGpio <> nil then<br />
begin<br />
fpMUnmap(FMap.FGpio,PAGE_SIZE);<br />
FMap.FGpio := Nil;<br />
end;<br />
if FMap.FClk <> nil then<br />
begin<br />
fpMUnmap(FMap.FClk,PAGE_SIZE);<br />
FMap.FClk := Nil;<br />
end;<br />
if FMap.FPwm <> nil then<br />
begin<br />
fpMUnmap(FMap.FPwm ,PAGE_SIZE);<br />
FMap.FPwm := Nil;<br />
end;<br />
end;<br />
//*******************************************************************************<br />
function TIoDriver.CreatePort(PortGpio, PortClk, PortPwm: LongWord): TIoPort;<br />
begin<br />
Result := TIoPort.Create;// new io port, pascal calls new fpMap, where offst is page sized 4096 bytes!!!<br />
Result.FGpio := FpMmap(Nil, PAGE_SIZE, PROT_READ or PROT_WRITE, MAP_SHARED, fd, PortGpio); // port config gpio memory<br />
Result.FClk:= FpMmap(Nil, PAGE_SIZE, PROT_READ or PROT_WRITE, MAP_SHARED, fd, PortClk);; // port clk<br />
Result.FPwm:= FpMmap(Nil, PAGE_SIZE, PROT_READ or PROT_WRITE, MAP_SHARED, fd, PortPwm);; // port pwm<br />
end;<br />
//*******************************************************************************<br />
procedure TIoPort.SetPinMode(gpin, mode: byte);<br />
var<br />
fSel, shift, alt : byte;<br />
gpiof, clkf, pwmf : ^LongWord;<br />
begin<br />
fSel := (gpin div 10)*4 ; //Select Gpfsel 0 to 5 register<br />
shift := (gpin mod 10)*3 ; //0-9 pin shift<br />
gpiof := Pointer(LongWord(Self.FGpio)+fSel);<br />
if (mode = INPUT) then<br />
gpiof^ := gpiof^ and ($FFFFFFFF - (7 shl shift)) //7 shl shift komplemens - Sets bits to zero = input<br />
else if (mode = OUTPUT) then<br />
begin<br />
gpiof^ := gpiof^ and ($FFFFFFFF - (7 shl shift)) or (1 shl shift);<br />
end<br />
else if (mode = PWM_OUTPUT) then<br />
begin<br />
Case gpin of<br />
12,13,40,41,45 : alt:= 4 ;<br />
18,19 : alt:= 2 ;<br />
else alt:= 0 ;<br />
end;<br />
If alt > 0 then<br />
begin<br />
gpiof^ := gpiof^ and ($FFFFFFFF - (7 shl shift)) or (alt shl shift);<br />
clkf := Pointer(LongWord(Self.FClk)+PWMCLK_CNTL);<br />
clkf^ := $5A000011 or (1 shl 5) ; //stop clock<br />
delayNanoseconds(200);<br />
clkf := Pointer(LongWord(Self.FClk)+PWMCLK_DIV);<br />
clkf^ := $5A000000 or (32 shl 12) ; // set pwm clock div to 32 (19.2/3 = 600KHz)<br />
clkf := Pointer(LongWord(Self.FClk)+PWMCLK_CNTL);<br />
clkf^ := $5A000011 ; //start clock<br />
Self.ClearBit(gpin);<br />
pwmf := Pointer(LongWord(Self.FPwm)+PWM_CONTROL);<br />
pwmf^ := 0 ; // Disable PWM<br />
delayNanoseconds(200);<br />
pwmf := Pointer(LongWord(Self.FPwm)+PWM0_RANGE);<br />
pwmf^ := $400 ; //max: 1023<br />
delayNanoseconds(200);<br />
pwmf := Pointer(LongWord(Self.FPwm)+PWM1_RANGE);<br />
pwmf^ := $400 ; //max: 1023<br />
delayNanoseconds(200);<br />
// Enable PWMs<br />
pwmf := Pointer(LongWord(Self.FPwm)+PWM0_DATA);<br />
pwmf^ := 0 ; //start value<br />
pwmf := Pointer(LongWord(Self.FPwm)+PWM1_DATA);<br />
pwmf^ := 0 ; //start value<br />
pwmf := Pointer(LongWord(Self.FPwm)+PWM_CONTROL);<br />
pwmf^ := PWM0_ENABLE or PWM1_ENABLE ;<br />
end;<br />
end;<br />
end;<br />
//*******************************************************************************<br />
procedure TIoPort.SetBit(gpin : byte);<br />
var<br />
gpiof : ^LongWord;<br />
begin<br />
gpiof := Pointer(LongWord(Self.FGpio) + 28 + (gpin shr 5) shl 2);<br />
gpiof^ := 1 shl gpin;<br />
end;<br />
//*******************************************************************************<br />
procedure TIoPort.ClearBit(gpin : byte);<br />
var<br />
gpiof : ^LongWord;<br />
begin<br />
gpiof := Pointer(LongWord(Self.FGpio) + 40 + (gpin shr 5) shl 2);<br />
gpiof^ := 1 shl gpin;<br />
end;<br />
//*******************************************************************************<br />
function TIoPort.GetBit(gpin : byte):boolean;<br />
var<br />
gpiof : ^LongWord;<br />
begin<br />
gpiof := Pointer(LongWord(Self.FGpio) + 52 + (gpin shr 5) shl 2);<br />
if (gpiof^ and (1 shl gpin)) = 0 then Result := False else Result := True;<br />
end;<br />
//*******************************************************************************<br />
procedure TIoPort.SetPullMode(gpin, mode: byte);<br />
var<br />
pudf, pudclkf : ^LongWord;<br />
begin<br />
pudf := Pointer(LongWord(Self.FGpio) + 148 );<br />
pudf^ := mode; //mode = 0, 1, 2 :Off, Down, Up<br />
delayNanoseconds(200);<br />
pudclkf := Pointer(LongWord(Self.FGpio) + 152 + (gpin shr 5) shl 2);<br />
pudclkf^ := 1 shl gpin ;<br />
delayNanoseconds(200);<br />
pudf^ := 0 ;<br />
pudclkf^ := 0 ;<br />
end;<br />
//*******************************************************************************<br />
procedure TIoPort.PwmWrite(gpin : byte; value : Longword);<br />
var<br />
pwmf : ^LongWord;<br />
port : byte;<br />
begin<br />
Case gpin of<br />
12,18,40 : port:= PWM0_DATA ;<br />
13,19,41,45 : port:= PWM1_DATA ;<br />
else exit;<br />
end;<br />
pwmf := Pointer(LongWord(Self.FPwm) + port);<br />
pwmf^ := value and $FFFFFBFF; // $400 complemens<br />
end;<br />
//*******************************************************************************<br />
end. <br />
<br />
</syntaxhighlight><br />
<br />
''Controlling Lazarus unit:(Project files[https://drive.google.com/folderview?id=0B-b-5ooIWPvGU043ZTZ0cUc2YkE&usp=sharing])''<br />
<syntaxhighlight lang="pascal"><br />
unit Unit1;<br />
<br />
{Demo application for GPIO on Raspberry Pi}<br />
{Inspired by the Python input/output demo application by Gareth Halfacree}<br />
{written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5}<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
ExtCtrls, Unix, PiGpio;<br />
<br />
type<br />
<br />
{ TForm1 }<br />
<br />
TForm1 = class(TForm)<br />
GPIO25In: TButton;<br />
SpeedButton: TButton;<br />
LogMemo: TMemo;<br />
GPIO23switch: TToggleBox;<br />
Timer1: TTimer;<br />
GPIO18Pwm: TToggleBox;<br />
Direction: TToggleBox;<br />
procedure DirectionChange(Sender: TObject);<br />
procedure GPIO18PwmChange(Sender: TObject);<br />
procedure GPIO25InClick(Sender: TObject);<br />
procedure FormActivate(Sender: TObject);<br />
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
procedure GPIO23switchChange(Sender: TObject);<br />
procedure SpeedButtonClick(Sender: TObject);<br />
procedure Timer1Timer(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
const<br />
INPUT = 0;<br />
OUTPUT = 1;<br />
PWM_OUTPUT = 2;<br />
LOW = False;<br />
HIGH = True;<br />
PUD_OFF = 0;<br />
PUD_DOWN = 1;<br />
PUD_UP = 2;<br />
// Convert Raspberry Pi P1 pins (Px) to GPIO port<br />
P3 = 0;<br />
P5 = 1;<br />
P7 = 4;<br />
P8 = 14;<br />
P10 = 15;<br />
P11 = 17;<br />
P12 = 18;<br />
P13 = 21;<br />
P15 = 22;<br />
P16 = 23;<br />
P18 = 24;<br />
P19 = 10;<br />
P21 = 9;<br />
P22 = 25;<br />
P23 = 11;<br />
P24 = 8;<br />
P26 = 7;<br />
<br />
var<br />
Form1: TForm1;<br />
GPIO_Driver: TIoDriver;<br />
GpF: TIoPort;<br />
PWM :Boolean;<br />
i, d : integer;<br />
Pin,Pout,Ppwm : byte;<br />
<br />
implementation<br />
<br />
{$R *.lfm}<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.FormActivate(Sender: TObject);<br />
begin<br />
if not GPIO_Driver.MapIo then<br />
begin<br />
LogMemo.Lines.Add('Error mapping gpio registry');<br />
end<br />
else<br />
begin<br />
GpF := GpIo_Driver.CreatePort(GPIO_BASE, CLOCK_BASE, GPIO_PWM);<br />
end ;<br />
Timer1.Enabled:= True;<br />
Timer1.Interval:= 25; //25 ms controll interval<br />
Pin := P22;<br />
Pout := P16;<br />
Ppwm := P12;<br />
i:=1;<br />
GpF.SetPinMode(Pout,OUTPUT);<br />
GpF.SetPinMode(Pin,INPUT);<br />
GpF.SetPullMode(Pin,PUD_Up); // Input PullUp High level<br />
end;<br />
<br />
procedure TForm1.GPIO25InClick(Sender: TObject);<br />
begin<br />
If GpF.GetBit(Pin) then LogMemo.Lines.Add('In: '+IntToStr(1))<br />
else LogMemo.Lines.Add('In: '+IntToStr(0));<br />
end;<br />
<br />
procedure TForm1.GPIO18PwmChange(Sender: TObject);<br />
begin<br />
if GPIO18Pwm.Checked then<br />
begin<br />
GpF.SetPinMode(Ppwm,PWM_OUTPUT);<br />
PWM := True; //PWM on<br />
end<br />
else<br />
begin<br />
GpF.SetPinMode(Ppwm,INPUT);<br />
PWM := False; //PWM off<br />
end;<br />
end;<br />
<br />
procedure TForm1.DirectionChange(Sender: TObject);<br />
begin<br />
if Direction.Checked then d:=10 else d:=-10;<br />
end;<br />
<br />
<br />
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);<br />
begin<br />
GpF.SetPinMode(Ppwm,INPUT);<br />
GpF.ClearBit(Pout);<br />
GpIo_Driver.UnmapIoRegisrty(GpF);<br />
end;<br />
<br />
procedure TForm1.GPIO23switchChange(Sender: TObject);<br />
<br />
Begin<br />
Timer1.Enabled := False;<br />
if GPIO23switch.Checked then<br />
begin<br />
GpF.SetBit(Pout); //Turn LED on<br />
end<br />
else<br />
begin<br />
GpF.ClearBit(Pout); //Turn LED off<br />
end;<br />
Timer1.Enabled := True;<br />
end;<br />
<br />
procedure TForm1.SpeedButtonClick(Sender: TObject);<br />
var<br />
i,p,k,v: longint;<br />
ido:TDateTime;<br />
begin<br />
ido:= Time;<br />
k:= TimeStampToMSecs(DateTimeToTimeStamp(ido));<br />
LogMemo.Lines.Add('Start: '+TimeToStr(ido));<br />
p:=10000000 ;<br />
For i:=1 to p do Begin<br />
GpF.SetBit(P16);<br />
GpF.ClearBit(P16);<br />
end;<br />
ido:= Time;<br />
v:= TimeStampToMSecs(DateTimeToTimeStamp(ido));<br />
LogMemo.Lines.Add('Stop: '+TimeToStr(ido)+' Frequency: '+<br />
IntToStr(p div (v-k))+' kHz');<br />
end;<br />
<br />
procedure TForm1.Timer1Timer(Sender: TObject);<br />
begin<br />
If PWM then Begin<br />
If (d > 0) and (i+d < 1024) then begin<br />
i:=i+d;<br />
GpF.PwmWrite(Ppwm,i);<br />
end ;<br />
If (d < 0) and (i+d > -1) then begin<br />
i:=i+d;<br />
GpF.PwmWrite(Ppwm,i);<br />
end;<br />
end;<br />
end;<br />
<br />
<br />
end. <br />
<br />
</syntaxhighlight><br />
<br />
=== PXL (Platform eXtended Library) for low level native access to GPIO, I²C, SPI, PWM, UART, V4L2, displays and sensors ===<br />
<br />
* Easy and fast access to GPIO, I²C, SPI, PWM, UART and high precision CPU timer.<br />
* V4L2 video/image capture using onboard and USB cameras. Serial cameras such as VC0706 and LSY201.<br />
* OpenGL ES hardware rendering with and without X server. Software rendering.<br />
* I²C and SPI displays such as SSD1306, SSD1351, PCB8544 (Nokia), HX8357 and ILI9340.<br />
* Character LCDs with direct pin wiring.<br />
* Support for Software SPI and UART (bit-banging).<br />
* Support for SC16IS7x0 (UART controller connected via I²C or SPI), including extra GPIO pins.<br />
* Support for sensors such as BMP180, DHT22, L3GD20, LSM303 and SHT10.<br />
* Other features like networking, math, image transformation, effects, drawing primitives...<br />
''More info and download of [http://asphyre.net/products/pxl PXL library].''<br />
<br />
''The PinDrive property is not implemented in the latest download from Asphyre so I am posting the code for my implementation in case anyone needs it. I found that the definition for TPinDrive is incorrect for the BCM2837 chip so I added some code to change it. This could be accomplished more easily by changing the definition but that file could conceivably be used for multiple chips (and I don't control the code) so I made all changes in the code to set the drive mode.''<br />
<br />
<syntaxhighlight lang="pascal"><br />
<br />
procedure TFastGPIO.SetPinDrive(const Pin: TPinIdentifier; const Value: TPinDrive);<br />
const<br />
GPPUDadr = $94;<br />
GPPclkadr = $98;<br />
type<br />
TBCM28PinDrive = (drvNone, drvPullDown, drvPullUp);<br />
var<br />
dValue: TBCM28PinDrive;<br />
PinBCM: TPinIdentifier;<br />
GPPUD, GPPclk: Pointer;<br />
begin<br />
case Value of<br />
TPinDrive.PullUp: dValue := TBCM28PinDrive.drvPullDown;<br />
TPinDrive.PullDown: dValue := TBCM28PinDrive.drvPullUp;<br />
else<br />
dValue := TBCM28PinDrive.drvNone;<br />
<br />
PinBCM := ProcessPinNumber(Pin);<br />
<br />
GPPUD := GetOffsetPointer(GPPUDadr);<br />
WriteMemSafe(GPPUD, Cardinal(dValue));<br />
FSystemCore.MicroDelay(1);<br />
<br />
GPPclk := GetOffsetPointer(GPPclkadr + (Cardinal(PinBCM) div 32) * 4);<br />
WriteMemSafe(GPPclk, 1 shl (PinBCM mod 32));<br />
FSystemCore.MicroDelay(1);<br />
<br />
WriteMemSafe(GPPUD, 0);<br />
WriteMemSafe(GPPclk, 0);<br />
end;<br />
<br />
</syntaxhighlight><br />
<br />
== References ==<br />
* Eben Upton and Gareth Halfacree. Raspberry Pi User Guide. John Wiley and Sons, Chichester 2012, ISBN 111846446X<br />
* B. J. Rao (2013) The Raspberry Pi, Pi Vision and Lazarus/FPC. [http://www.blaisepascal.eu Blaise Pascal Magazine]. 29: 14-21.<br />
<br />
== External and Other Links ==<br />
<br />
* If you want to install the latest stable release of fpc and, additional and isolated, the trunk fpc compiler: you can read the following guide.<br />
It was written using gentoo but this guide will be useful with any distro: [[Install fpc on Raspberry with Gentoo]] (very out of date)<br />
<br />
* [http://www.raspberrypi.org Raspberry Pi Foundation]<br />
* [http://www.raspberrypi-spy.co.uk Raspberry Pi Spy: Raspberry Pi tutorials, scripts, help and downloads]<br />
* [http://www.lazarus.freepascal.org/index.php/topic,17404.0.html Lazarus wrapper unit for Gordon Henderson's wiringPi C library]<br />
* [https://github.com/laz2wiringpi/laz2wiringpi Another wiringPi wrapper with waitForInterrupt() and wiringPiISR()]<br />
* [https://forum.lazarus.freepascal.org/index.php/topic,17404.msg405237.html#msg405237 Yet another wiringPi wrapper with waitForInterrupt(), wiringPiISR(), i2c, spi and serial]<br />
* [https://projects.drogon.net/raspberry-pi/wiringpi/pins/ Pin layout of the wiringPi library]<br />
* [https://github.com/WiringPi/WiringPi wiringPi fork updated for latest hardware]<br />
* [http://www.elinux.org/Lazarus_on_RPi Additional information on Lazarus and Raspberry Pi at eLinux.org]<br />
* [http://www.pp4s.co.uk/main/gs-pi-intro.html Getting Started with Pascal on the Pi] (noted dead link in late 2023)<br />
* [http://www.pp4s.co.uk/main/gs-pi-gpio-intro.html Getting Started with Led, Switch and DC motor on Pi] (noted dead link in late 2023)<br />
* [https://github.com/laz2wiringpi/lazI2cdev lazi2cdev - General i2c with drivers for ADS1015 ADC, MCP23017 IO, MCP4725 DAC, PCA9685 PWM and HD44780 LCD]<br />
* [http://superbitysoft.co.uk/lazberrypi/ Lazberry Pi: Comprehensive information on Lazarus on the Raspberry Pi computer]. (noted dead link in late 2023)<br />
* Stefan Fischer's [https://github.com/rudiratlos/rpi-hal rpi_hal library] and [http://www.lazarus.freepascal.org/index.php/topic,20991.0.html rpi_hal discussion topic] on Lazarus forum.<br />
* [http://www.meltonisl.com/pigpio.pas Improved Lazarus Unit for fast access to GPIO].<br />
* [http://asphyre.net/products/pxl PXL - Platform eXtended Library].<br />
* [https://github.com/zipplet/rpiio RPIIO - Raspberry Pi GPIO and I2C library].</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Lazarus_3.0_release_notes&diff=157938Lazarus 3.0 release notes2023-12-21T23:55:25Z<p>Dbannon: type</p>
<hr />
<div><br />
== LCL Interfaces Changes ==<br />
<br />
=== Cocoa ===<br />
* IME fully supported, such as Chinese/Japanese/Korean, DeadKeys, Emoji & Symbols.<br />
* Multi Displays/Monitors fully supported.<br />
* Docking fully supported, including the IDE.<br />
* Cursor completely refactored and significantly improved, compatible with macOS Ventura.<br />
* TPageControl significantly improved.<br />
* Many controls improved (TComboxBox/TListBox/TDateTimePicker/TStaticText/TSpeedButton/TPanel etc.)<br />
* Many memory leaks fixed.<br />
<br />
=== Qt ===<br />
* Implemented TCheckBox.Alignment and TRadioButton.Alignment.<br />
* Implemented TCustomComboBox.AdjustDropDown and TCustomComboBox.ItemWidth.<br />
<br />
=== Qt5 ===<br />
* Qt5 uses native event loop on all platforms. C bindings are updated. Minimum C bindings version for lazarus 3.0 is 1.2.15.<br />
* Note that most Linux Distributions will not have an appropriate libqt5pas library until their next release after the formal release of Lazarus 3.0. Build your own from the Lazarus source tree or download from https://github.com/davidbannon/libqt5pas/releases/latest<br />
* Implemented TCheckBox.Alignment and TRadioButton.Alignment.<br />
* Implemented TCustomComboBox.AdjustDropDown and TCustomComboBox.ItemWidth.<br />
<br />
=== Qt6 ===<br />
* Qt6 widgetset implemented. C bindings are based on Qt6 6.2.0 LTS. Minimum C bindings version for lazarus 3.0 is 6.2.7.<br />
* Note that most Linux Distributions will not have an appropriate libqt6pas library until their next release after the formal release of Lazarus 3.0. Build your own from the Lazarus source tree or download from https://github.com/davidbannon/libqt6pas/releases/latest<br />
* Implemented TCheckBox.Alignment and TRadioButton.Alignment.<br />
* Implemented TCustomComboBox.AdjustDropDown and TCustomComboBox.ItemWidth.<br />
<br />
=== Gtk3 ===<br />
* Gtk3 pascal bindings are completely reworked.<br />
* A number of stability improvements.<br />
* Now requires GTK >= 3.24.24 and Glib2.0 >= 2.66<br />
<br />
== LCL Changes ==<br />
=== TCustomImageList ===<br />
TCustomImageList made more extensible:<br />
# Protected MarkAsChanged method is added, which sets FChanged to true (this allows to make custom triggers for OnChange event).<br />
# Virtual protected DoAfterUpdateStarted and DoBeforeUpdateEnded methods are added. They are called in first BeginUpdate and last EndUpdate respectively.<br />
<br />
=== TTaskDialog ===<br />
*Old behaviour Win32: A placeholder icon was used for FooterIcon = tdiNone and MainIcon = tdiNone.<br />
*New behaviour Win32: No icon is used for FooterIcon = tdiNone and MainIcon = tdiNone.<br />
*Reason: Removing drawing glitch. The text move over to allow more content and better alignment. See {{MantisLink|39172}}<br />
<br />
=== TSpeedButton ===<br />
*Old behaviour: Multi-line captions could only be entered by code. And multi-line captions were always left-aligned.<br />
*New behaviour: Multi-line captions can also be entered in the object inspector. New property Alignment to specify whether the caption should be left-/right-aligned or centered. Default: centered, like in Delphi.<br />
*Reason: Better usability.<br />
<br />
=== TLabel.Transparent, .Color and .ParentColor changes ===<br />
*Old behaviour: Transparent property was bound to Color=clNone<br />
*New behaviour: Transparent is a standalone property<br />
*Reason: Delphi compatibility and to fix ParentColor issues.<br />
*Remedy: If you are setting the Color property, Transparent is not automatically switched from True to False now, you have to do it yourself. This is in compliance with Delphi and also solves problems with Color/ParentColor changes.<br />
<br />
=== TPanel.VerticalAlignment ===<br />
*Old behaviour: The panel caption was always centered vertically.<br />
*New behaviour: The new property VerticalAlignment (taAlignTop, taAlignBottom, taVerticalCenter) allows to place the caption also at the top or bottom of the panel interior.<br />
*Reason: Delphi compatibility and better usability<br />
<br />
=== TCalendar ===<br />
* Properties MinDate and MaxDate are implemented. These limits are only imposed if MaxDate > MinDate. Unfortunately GTK2/3 widgetsets do not support this, so selecting a date outside the MinDate/MaxDate range will still be possible there.<br />
<br />
=== TCheckbox, TRadioButton ===<br />
*Different calculation of checkbox/radiobutton size in order to correctly take care of Win-10 "ease of access" feature. See [https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/39398 issue #39398]<br />
*Consequence: lfm files will contain different sizes of these controls (if auto-sized) compared with earlier versions.<br />
<br />
=== Grids ===<br />
* You can now set the cell editors properties ParentColor and ParentFont by including goEditorParentColor resp. goEditorParentFont in the grid's Options2 property.<br />
<br />
=== TShellTreeView ===<br />
* New property <tt>ExpandCollapseMode</tt> defining options whether a collapsing node should clear its child nodes.<br />
* Implements custom sorting of the treeview items by setting <tt>FileSortType</tt> to <tt>fstCustom</tt> and providing a custom compare function in the event <tt>OnSortCompare</tt>.<br />
<br />
=== TShellListView ===<br />
* TShellListView now subclasses TListItem, so it can store file info in it. <br />
* If you use OnCreateItemClass to create your own descendant of TListItem, it may be advisable to base your own class on TShellListItem instead of on TListItem. This way you will also have access to the TShellListItem's FileInfo property.<br />
<br />
=== TTreeView ===<br />
* Adds ShowSeparators as a published property like ShowLines and ShowRoot.<br />
<br />
=== TTrayIcon gtk2 Only ===<br />
If using the gtk2 tray icon, a new global variable is available that will allow you to predetermine which of the two TrayIcon models you use. See the Wiki for details. This is, and will only ever be, gtk2 only.<br />
<br />
== IDE Changes ==<br />
=== Character map ===<br />
* Resizable characters to improve readability.<br />
* The character map was split off from the IDE and moved to separate packages. The designtime package is installed by default so that there is no difference in the IDE. Now users can access it in their own applications after adding the runtime package "charactermappkg.lpk" to the project requirements.<br />
<br />
=== Debugger ===<br />
<br />
==== Project Options ====<br />
<br />
* "Run(F9)" can either be "Debug" or "Run without debug".<br />
: In newly created Debug/Release modes, the Release mode will no longer invoke the debugger by default.<br />
: Existing Debug/Release modes must be edited, to enable this.<br />
* Per project "Debugger backend" settings. In addition to choosing a specific backend from the global IDE settings, a backend can be configured just for the project (i.e. special gdb-server settings)<br />
<br />
==== IDE Dialogs ====<br />
<br />
* Improved <u>Watches window</u><br />
** Expand/Unfold for classes, records, etc<br />
** Expand/Unfold with paged browser for arrays<br />
** Drag and Drop to reorder watches<br />
** Drag and Drop to create new "top level" watches from nested entries (in expand/unfolded lists)<br />
** Address column for types with internal pointer (classes, long-string, dyn-array, (real) pointer)<br />
* Improved <u>Locals window</u><br />
** Expand/Unfold for classes, records, etc<br />
** Expand/Unfold with paged browser for arrays<br />
** Address column for types with internal pointer (classes, long-string, dyn-array, (real) pointer)<br />
** Power button<br />
* Improved <u>Inspect window</u><br />
** Fixed: Updating value when context changes<br />
** Added options for Function calling / and "Converter" (See FpDebug: SysVarToLstr)<br />
** Added filter to search for text in name or value.<br />
** Added ctrl-Up/Down/Page-Up/Page-Down to navigate the grid.<br/>Alt-Left/Right for history. And ctrl-Enter to select.<br />
* Improved <u>Evaluate/Modify window</u><br />
** New Layout<br />
** Added DisplayFormat<br />
** Added options for Function calling / and "Converter" (See FpDebug: SysVarToLstr)<br />
* Improved <u>Assembler window</u><br />
** Added history navigation (forward/backward)<br />
** For FpDebug: added annotations to jump/call targets, and allow to ctrl-click to disassemble target address<br />
<br />
==== FpDebug / LazDebuggerFp ====<br />
<br />
* Improved "function calling" in watch eval. See [[FpDebug-Watches-FunctionEval]]<br />
* %RAX Accessing cpu registers in watch expression (only full registers, not yet AH or AL or EAX on 64bit) <br />
* Intrinsic functions: [[FpDebug-Watches-Intrinsic-Functions]]<br />
* Intrinsic/extended operators: MyArray[1..3] array slice with operator mapping. [[FpDebug-Watches-Intrinsic-Functions#Intrinsic_Operators]]<br />
* Option to detect "variant" and call "SysVarToLStr" in the target app.<br />
* Suspend/Resume individual threads (must be done while app is paused, and will be applied for subsequent step/run)<br />
* Partial improvements to debug in DLL: https://wiki.freepascal.org/Debugger_Status#Other<br />
* Disassembler now annotates lines for call/jmp/jne/... with info on the target address (function name, file, line)<br />
* F7/F8 Step-Into/Over can now be used to start the debugger and run to the first line of the main program begin/end.<br />
<br />
==== LazDebuggerFpLldb (default on MacOS) ====<br />
<br />
* Added Mem-Limits (and String,Pchar,Array) to the debugger config<br />
: See https://wiki.freepascal.org/LazDebuggerFp (MaxMemReadSize, MaxStringLen, MaxArrayLen, MaxNullStringSearchLen)<br />
: Limiting the default results for watches/locals/stack-params,... can prevent slow evaluation.<br />
: Arrays can then be browsed in the watches window, using the new "paged browser for arrays" (expand via [+])<br />
<br />
=== Floating point properties in the Object Inspector ===<br />
* The Object Inspector now explicitely disallows to set a floating point property's value to +/-Inf or NaN.<br />
* Reason: whilst +/-Inf and NaN are valid values for a floating point property, they cannot be streamed (so, the form could not be loaded) and setting it to NaN caused havoc in the IDE.<br />
* Remedy: set the value at runtime (in code)<br />
<br />
=== Reading unit names in lfm ===<br />
<br />
FPC 3.3.1 component writer supports optionally writing types with their unit names as ''unitname/type''. Lazarus can now read this.<br />
<br />
=== Ambiguous Classtypes ===<br />
<br />
You can now register two component classes with the same name, e.g. ''fresnel.TButton'' and ''StdCtrls.TButton''. You can even put both on the same form.<br />
<br />
=== Editor ===<br />
<br />
- Highlight for PasDoc<br />
<br />
=== IDE Options ===<br />
<br />
In Tools -> Options -> Environment -> Window page these three settings are now <b>ON</b> by default :<br />
* IDE title starts with project name<br />
* IDE title shows project directory<br />
* IDE title shows selected build mode<br />
It has been requested by many users. If the IDE's title bar has no info about the active project, a user must open Project Inspector or Project Options to see it, which is inconvenient.<br />
<br />
=== Lazarus Examples ===<br />
A new approach to Examples that copies an Example to a fresh working area (avoiding the Linux Read Only Problem) and, possibly an easier to use search model.<br />
<br />
== IDE Interface Changes ==<br />
<br />
== Components ==<br />
=== TAChart ===<br />
* The '''TLegendClickTools''' now is able to detect clicks on series legend items and reports the clicked series in the new '''OnSeriesClick''' event.<br />
* New '''TDatapointMarksClickTool''' which becomes active when the user clicked on the marks of a series.<br />
* New property '''TickWidth''' for the chart axes.<br />
* New property '''FullWidth''' for the chart title and footer to run their background across the entire chart width.<br />
* New property '''RandomColors''' for <tt>TRandomChartSource</tt>.<br />
* New properties '''YIndexWhiskerMin''', '''YIndexBarMin''', '''YIndexCenter''', '''YIndexBarMax''', '''YIndexWhiskerMax''', and '''YDataLayout''' in TBoxAndWhiskerSeries for more flexible assignment of y values to the parts of the box/whisher shape.<br />
* New option '''aipInteger''' in the set TAxisIntervalParamOptions which sets axis labels only at integer values and thus supresses the unwanted intermediate labels in bar charts and helps to enforce labels in logarithmic plots at the powers of the logarithmic base (usually 10).<br />
* New event '''OnAddStyleToLegend''' for TChartStyles. It has a boolean parameter <tt>AddToLegend</tt> with which you can determine whether the series level using this style is displayed in the legend.<br />
<br />
=== TDateTimePicker ===<br />
* New properties MonthDisplay and CustomMonthNames. They are meant to replace the MonthNames property, which has been deprecated.<br />
* New property DecimalSeparator. Allows a user-specified value to be used instead of a hard-coded Colon character.<br />
<br />
=== TDBDateTimePicker ===<br />
* Adds Options as a published property.<br />
* Publishes the DecimalSeparator property.<br />
* Publishes the missing Alignment property. Consistent with TDateTimePicker,<br />
<br />
=== T(Float)SpinEditEx ===<br />
* New property '''Orientation''' which allows to arrange the spin buttons horizontally.<br />
<br />
=== TCheckListBox ===<br />
* Adds HeaderColor and HeaderBackgroundColor properties. Used on list items where the Header property is enabled. Implemented for the Win32 widget set.<br />
<br />
=== Pas2js ===<br />
* lazbuild now can compile pas2js projects by passing the environment variable PAS2JS with the path of the pas2js executable.<br />
* Project groups with pas2js projects now can compile without being opened.<br />
* New project type [[lazarus_pas2js_integration#Progressive_Web_Application|Progressive Web Application]]<br />
* New project type [[lazarus_pas2js_integration#Electron_Web_Application|Electron Web Application]]<br />
* pas2jsdsgn now uses the SimpleWebServerGUI package, replacing its own http server controller.<br />
* F9, Run now builds, starts a HTTP server and a browser<br />
<br />
=== Lazarus Icon Collection ===<br />
* Not a component, but the Lazarus installation now contains a folder with general-purpose icons for usage in toolbars, menus, buttons etc. of any GUI applications (folder ''images/general_purpose'').<br />
* The images come in various sizes and thus are compatible with the scaled image list of Lazarus v2.0+.<br />
* Author: Roland Hahn (https://www.rhsoft.de/).<br />
* License: Creative Commons CC0 (no restrictions in usage).<br />
<br />
=== gir2pascal ===<br />
<br />
* Sources of [[gir2pascal]] (a tool to convert [https://gi.readthedocs.io/ GObject Introspection] descriptions to Pascal files) are now included in Lazarus source tree ([https://gitlab.com/freepascal.org/lazarus/lazarus/-/tree/main/tools/gir2pascal tools/gir2pascal] directory) and maintained there.<br />
<br />
=== lazdelphi ===<br />
<br />
An IDE addon adding a parser for the Delphi compiler errors and hints. You can run dcc32.exe as external tool or '''execute before''' command in the compiler options and use the ''Delphi Compiler'' parser for the output, so that the errors/hints in the Messages window can open the source. See [[Lazarus Delphi Compiler Tool]]<br />
<br />
=== Jedi Code Format ===<br />
* Some improvements in code formatting.<br />
* The jcf command line tool is now a text-mode application and no longer requires XWindow on linux to run.<br />
<br />
=== DockedFormEditor ===<br />
<br />
In case you are still using the sparta docked form editor, use the '''dockedformeditor''' package instead.<br />
<br />
== Changes affecting compatibility ==<br />
<br />
===IDE Settings===<br />
<br />
====Run > Run Parameters====<br />
The Working-Dir and the Launch-/Host-App can now be specified relative to the Project-Dir.<br />
As a result of this, and due to inconsistencies between "Run in debugger" and "Run without debug" some details of how those fields are resolved changed.<br />
<br />
In some cases you may therefore have to adjust your settings.<br />
<br />
;The Working-dir is now determined as follows:<br />
<br />
# BuildMode.RunParams "working directory" set by user (New, this can now be relative to project dir)<br />
# Project Dir, if not virtual<br />
# Directory from Host-App (RunParams), or if (and only if) Host-App is empty from Project.exe (If host app is in %PATH, then there is no "working directory")<br />
<br />
The first 2 steps are the same as before the change.<br />
The 3rd step was previously only used for "debugging", but "run without debug" did use: "Launch-App", "Host-App", Project.exe<br />
<br />
;Change: The path of the "Launch App" is no longer considered<br />
<br />
; The Launch-/Host-App location are now determined as follows:<br />
# An app with absolute path is used as given<br />
# A relative path (including no path at all) is resolved as relative to the Project-dir.<br />
# An app without any path at all (if not found in step 2) is searched in the %PATH environment.<br />
<br />
;Change: Search in %PATH was only done by "run without debug", but not by debug. It is now done by both.<br />
;Change: Checking for an exe relative to the project-dir was added.<br />
<br />
<br />
===LCL incompatibility===<br />
====TLabel: autosized and right-aligned====<br />
*Old behaviour: Autosized label with Alignment=taRightJustify but Anchors=[akLeft,...] grew to left.<br />
*New behaviour: The label grows to right now.<br />
*Reason: It wasn't possible to implement the behavior also for hidden labels without significant extensions in the LCL. The LCL has a different and more generic feature of control-based anchoring that delivers the same effect (see Remedy down), so it is not needed and wanted to double this feature and make the LCL code more complex and prone to bugs.<br />
*Remedy: Use the LCL anchoring to a secondary control. Anchor the right side of the label to another control. Then the autosized label will grow to the left but won't move to the right when the parent is resized like it is done with a simple akRight anchor without a reference control.<br />
<br />
====TDateEdit/TTimeEdit====<br />
The value of NullDate has changed.<br><br />
Reason:<br />
* It was impossible to actually select the date corresponding to NullDate (30 dec 1899 by default) in the control.<br />
* Remedy (1): if your code depended on NullDate actually being 0.0, you have to adjust your code.<br />
* Remedy (2): if your code used NullDate for a TTimeEdit, change that to the new constant NullTime instead.<br />
* Note: NullDate is actually a writeable constant. This was kept for compatibility reasons. It is however a bad idea to change it's value to anything that is an actual date that is within the range of the control.<br />
<br />
====Cocoa====<br />
Some global configuration variables were moved from CocoaInt to CocoaConfig. such as CocoaBasePPI, CocoaIconUse, CocoaToggleBezel, CocoaToggleType etc.<br />
<br />
====GTK3====<br />
GTK3 is no longer supported on earlier Linuxes, such as Ubuntu 20.04. It requires GTK >= 3.24.24 and Glib2.0 >= 2.66<br />
<br />
===LazUtils===<br />
<br />
==== Masks unit ====<br />
The masks unit has been completely rewritten.<br><br />
Reasons:<br />
* speed: the old Matches() method had O(n^2) or even O(n^3) characteristics.<br />
* improved control over how the mask is interpreted.<br />
New types (for parameters) and a dedicated TMaskWindows class have been added.<br><br />
TMask.MatchesWindowsMask and the old TMaskOptions type have been deprecated and will be removed in the next release.<br><br />
===== Ranges and Sets =====<br />
* The old masks implementation supported sets, but not ranges. The new implementation supports both sets ([abc]) and ranges ([a-c]). As a consequence a '-' inside such a construct is now interpreted as part of the range definition, not as a literal '-'.<br />
* Reason: ranges are a good thing to have by default (the old implementation simply lacked this). We decided it's a small price to pay.<br />
* Remedy: either escape the '-' with EscapeChar (which defaults to '\') or exclude mocRange from the TMaskOpcodes parameter.<br />
<br />
===== Constructors do not fail anymore on an invalid mask =====<br />
* When providing an invalid mask to the old T(Windows)Mask(List) constructors an exception was raised.<br />
* The new constructors do not raise an exception in this case. Instead an exception is raised in when Matches() is called.<br />
* Reason: it's not very nice to have a constructor fail.<br />
<br />
==== Translations unit ====<br />
Added GetLanguageID function. It returns a record with language code (in ISO 639-1 or ISO 639-2) and country code (in ISO 3166) for current system locale.<br><br />
Added GetLanguageIDFromLocaleName function. It parses Unix locale name and returns a record with language code and country code. It is useful to parse language identifiers passed e. g. via command-line parameters.<br />
<br />
Implementation is based on GetLanguageIDs procedure from GetText unit, but is rewritten to have the following properties:<br />
* Language and country codes are always returned in ISO formats on Windows.<br />
* Unix locale identifier is properly parsed and language/country codes are properly extracted.<br />
* Don't assume that language code is always two-letter (ISO 639-1), it can have bigger length (e. g. three letters, like in ISO 639-2).<br />
* Locale ID is returned in a record type. This will allow to return additional fields in backwards-compatible manner in future. Currently it contains language code, country code and language ID (combination of language code and country code).<br />
<br />
These functions are used now throughout the Lazarus codebase. This greatly improves automatic language detection and loading of correct translations by Lazarus:<br />
* Three-letter (ISO 639-2) language identifiers are no more truncated to two letters. Thus, translations for such languages will be correctly loaded when available.<br />
* Previously on Windows some language and country codes were obtained in non-ISO format, which prevented correct loading of some translations, e. g. Chinese (zh_CN).<br />
* On Unix translations with country codes, like Brazilian Portuguese (pt_BR) or Chinese (zh_CN) are correctly loaded now.<br />
* macOS is now handled as any other Unix. This removes dependency on language list in Lazarus bundle (which had to be maintained manually) and thus fixes loading of Czech, Hungarian, Brazilian Portuguese, Ukrainian translations.<br />
<br />
==== LazUTF8 unit ====<br />
* Deprecated LazGetLanguageIDs (returns combination of language and country codes) and LazGetShortLanguageID (returns only language code) procedures.<br />
* Reason: this functionality belongs to Translations unit (calls of these procedures are almost always followed by calls to procedures from Translations unit), and these procedures are now thin wrappers of GetLanguageID function from Translations unit.<br />
* Remedy: use GetLanguageID function from Translations unit.<br />
<br />
==== LazUTF8Classes and LazUTF8SysUtils units ====<br />
Everything in these units was deprecated for a long time and now they were removed.<br />
LazUTF8SysUtils was earlier renamed to LazSysUtils and this deprecated version was left for a transit period.<br />
* Class TStringListUTF8 can be replaced with TStringList.<br />
* Class TMemoryStreamUTF8 can be replaced with TMemoryStream.<br />
* Global procedure LoadStringsFromFileUTF8 can be replaced with TStrings.LoadFromFile.<br />
* Global procedure SaveStringsToFileUTF8 can be replaced with TStrings.SaveToFile.<br />
* Functions NowUTC and GetTickCount64 can be found in LazSysUtils.<br />
<br />
===Components incompatibility===<br />
====LazControls====<br />
======TSpinEditExBase derived classes======<br />
* All derived classes form TSpinEditExBase must implement a SameValue method. This method is defined as an <b>abstract</b> method in TSpinEditExBase. <br />
* Reason: All derived classes used Math.SameValue. This is wrong for comparing integer types (even if is is safe when comparing relative small values).<br />
* Remedy: unfortunately you'll have to adjust your code.<br />
======TFloatSpinEditEx======<br />
* The property NumbersOnly is no longer published.<br />
* Reason: the property makes no sense for this control and only confuses users.<br />
* Remedy: if you really need NumbersOnly to be True, you must set it in code.<br />
====FpVectorial====<br />
* The <tt>Size</tt> element in the FPVectorial <tt>TvFont</tt> record is a floating point value now (type <tt>double</tt>).<br />
* Reason: Avoid rounding errors because the drawing coordinates are <tt>double</tt> already.<br />
* Remedy: There is rarely a chance that this change will have an effect on user code. Only when the font size is stored in a variable it must be declared as <tt>double</tt> rather than as <tt>integer</tt>.<br />
====TurboPower_ipro====<br />
* Type declarations and the html nodes were moved from unit IpHtml to separate units, IpHtmlTypes, IpHtmlClasses and IpHtmlNodes. This may break compilation of existing projects.<br />
* Reason: Improve maintainability of the extremely long unit IpHtml<br />
* Remedy: Add IpHtmlTypes, IpHtmlClasses and/or IpHtmlNodes to the uses clause of the project unit(s) when an "identifier not found" error referring to this package is reported by the compiler.<br />
<br />
== Other release notes ==<br />
<br />
{{Navbar Lazarus Release Notes}}<br />
<br />
[[Category:Release Notes]]<br />
[[Category:Lazarus]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Lazarus_3.0_release_notes&diff=157937Lazarus 3.0 release notes2023-12-21T23:54:50Z<p>Dbannon: added mention of gtk2 global.</p>
<hr />
<div><br />
== LCL Interfaces Changes ==<br />
<br />
=== Cocoa ===<br />
* IME fully supported, such as Chinese/Japanese/Korean, DeadKeys, Emoji & Symbols.<br />
* Multi Displays/Monitors fully supported.<br />
* Docking fully supported, including the IDE.<br />
* Cursor completely refactored and significantly improved, compatible with macOS Ventura.<br />
* TPageControl significantly improved.<br />
* Many controls improved (TComboxBox/TListBox/TDateTimePicker/TStaticText/TSpeedButton/TPanel etc.)<br />
* Many memory leaks fixed.<br />
<br />
=== Qt ===<br />
* Implemented TCheckBox.Alignment and TRadioButton.Alignment.<br />
* Implemented TCustomComboBox.AdjustDropDown and TCustomComboBox.ItemWidth.<br />
<br />
=== Qt5 ===<br />
* Qt5 uses native event loop on all platforms. C bindings are updated. Minimum C bindings version for lazarus 3.0 is 1.2.15.<br />
* Note that most Linux Distributions will not have an appropriate libqt5pas library until their next release after the formal release of Lazarus 3.0. Build your own from the Lazarus source tree or download from https://github.com/davidbannon/libqt5pas/releases/latest<br />
* Implemented TCheckBox.Alignment and TRadioButton.Alignment.<br />
* Implemented TCustomComboBox.AdjustDropDown and TCustomComboBox.ItemWidth.<br />
<br />
=== Qt6 ===<br />
* Qt6 widgetset implemented. C bindings are based on Qt6 6.2.0 LTS. Minimum C bindings version for lazarus 3.0 is 6.2.7.<br />
* Note that most Linux Distributions will not have an appropriate libqt6pas library until their next release after the formal release of Lazarus 3.0. Build your own from the Lazarus source tree or download from https://github.com/davidbannon/libqt6pas/releases/latest<br />
* Implemented TCheckBox.Alignment and TRadioButton.Alignment.<br />
* Implemented TCustomComboBox.AdjustDropDown and TCustomComboBox.ItemWidth.<br />
<br />
=== Gtk3 ===<br />
* Gtk3 pascal bindings are completely reworked.<br />
* A number of stability improvements.<br />
* Now requires GTK >= 3.24.24 and Glib2.0 >= 2.66<br />
<br />
== LCL Changes ==<br />
=== TCustomImageList ===<br />
TCustomImageList made more extensible:<br />
# Protected MarkAsChanged method is added, which sets FChanged to true (this allows to make custom triggers for OnChange event).<br />
# Virtual protected DoAfterUpdateStarted and DoBeforeUpdateEnded methods are added. They are called in first BeginUpdate and last EndUpdate respectively.<br />
<br />
=== TTaskDialog ===<br />
*Old behaviour Win32: A placeholder icon was used for FooterIcon = tdiNone and MainIcon = tdiNone.<br />
*New behaviour Win32: No icon is used for FooterIcon = tdiNone and MainIcon = tdiNone.<br />
*Reason: Removing drawing glitch. The text move over to allow more content and better alignment. See {{MantisLink|39172}}<br />
<br />
=== TSpeedButton ===<br />
*Old behaviour: Multi-line captions could only be entered by code. And multi-line captions were always left-aligned.<br />
*New behaviour: Multi-line captions can also be entered in the object inspector. New property Alignment to specify whether the caption should be left-/right-aligned or centered. Default: centered, like in Delphi.<br />
*Reason: Better usability.<br />
<br />
=== TLabel.Transparent, .Color and .ParentColor changes ===<br />
*Old behaviour: Transparent property was bound to Color=clNone<br />
*New behaviour: Transparent is a standalone property<br />
*Reason: Delphi compatibility and to fix ParentColor issues.<br />
*Remedy: If you are setting the Color property, Transparent is not automatically switched from True to False now, you have to do it yourself. This is in compliance with Delphi and also solves problems with Color/ParentColor changes.<br />
<br />
=== TPanel.VerticalAlignment ===<br />
*Old behaviour: The panel caption was always centered vertically.<br />
*New behaviour: The new property VerticalAlignment (taAlignTop, taAlignBottom, taVerticalCenter) allows to place the caption also at the top or bottom of the panel interior.<br />
*Reason: Delphi compatibility and better usability<br />
<br />
=== TCalendar ===<br />
* Properties MinDate and MaxDate are implemented. These limits are only imposed if MaxDate > MinDate. Unfortunately GTK2/3 widgetsets do not support this, so selecting a date outside the MinDate/MaxDate range will still be possible there.<br />
<br />
=== TCheckbox, TRadioButton ===<br />
*Different calculation of checkbox/radiobutton size in order to correctly take care of Win-10 "ease of access" feature. See [https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/39398 issue #39398]<br />
*Consequence: lfm files will contain different sizes of these controls (if auto-sized) compared with earlier versions.<br />
<br />
=== Grids ===<br />
* You can now set the cell editors properties ParentColor and ParentFont by including goEditorParentColor resp. goEditorParentFont in the grid's Options2 property.<br />
<br />
=== TShellTreeView ===<br />
* New property <tt>ExpandCollapseMode</tt> defining options whether a collapsing node should clear its child nodes.<br />
* Implements custom sorting of the treeview items by setting <tt>FileSortType</tt> to <tt>fstCustom</tt> and providing a custom compare function in the event <tt>OnSortCompare</tt>.<br />
<br />
=== TShellListView ===<br />
* TShellListView now subclasses TListItem, so it can store file info in it. <br />
* If you use OnCreateItemClass to create your own descendant of TListItem, it may be advisable to base your own class on TShellListItem instead of on TListItem. This way you will also have access to the TShellListItem's FileInfo property.<br />
<br />
=== TTreeView ===<br />
* Adds ShowSeparators as a published property like ShowLines and ShowRoot.<br />
<br />
=== TTrayIcon gtk2 Only ===<br />
If using the gtk2 tray, a new global variable is available that will allow you to predetermine which of the two TrayIcon models you use. See the Wiki for details. This is, and will only ever be, gtk2 only.<br />
<br />
== IDE Changes ==<br />
=== Character map ===<br />
* Resizable characters to improve readability.<br />
* The character map was split off from the IDE and moved to separate packages. The designtime package is installed by default so that there is no difference in the IDE. Now users can access it in their own applications after adding the runtime package "charactermappkg.lpk" to the project requirements.<br />
<br />
=== Debugger ===<br />
<br />
==== Project Options ====<br />
<br />
* "Run(F9)" can either be "Debug" or "Run without debug".<br />
: In newly created Debug/Release modes, the Release mode will no longer invoke the debugger by default.<br />
: Existing Debug/Release modes must be edited, to enable this.<br />
* Per project "Debugger backend" settings. In addition to choosing a specific backend from the global IDE settings, a backend can be configured just for the project (i.e. special gdb-server settings)<br />
<br />
==== IDE Dialogs ====<br />
<br />
* Improved <u>Watches window</u><br />
** Expand/Unfold for classes, records, etc<br />
** Expand/Unfold with paged browser for arrays<br />
** Drag and Drop to reorder watches<br />
** Drag and Drop to create new "top level" watches from nested entries (in expand/unfolded lists)<br />
** Address column for types with internal pointer (classes, long-string, dyn-array, (real) pointer)<br />
* Improved <u>Locals window</u><br />
** Expand/Unfold for classes, records, etc<br />
** Expand/Unfold with paged browser for arrays<br />
** Address column for types with internal pointer (classes, long-string, dyn-array, (real) pointer)<br />
** Power button<br />
* Improved <u>Inspect window</u><br />
** Fixed: Updating value when context changes<br />
** Added options for Function calling / and "Converter" (See FpDebug: SysVarToLstr)<br />
** Added filter to search for text in name or value.<br />
** Added ctrl-Up/Down/Page-Up/Page-Down to navigate the grid.<br/>Alt-Left/Right for history. And ctrl-Enter to select.<br />
* Improved <u>Evaluate/Modify window</u><br />
** New Layout<br />
** Added DisplayFormat<br />
** Added options for Function calling / and "Converter" (See FpDebug: SysVarToLstr)<br />
* Improved <u>Assembler window</u><br />
** Added history navigation (forward/backward)<br />
** For FpDebug: added annotations to jump/call targets, and allow to ctrl-click to disassemble target address<br />
<br />
==== FpDebug / LazDebuggerFp ====<br />
<br />
* Improved "function calling" in watch eval. See [[FpDebug-Watches-FunctionEval]]<br />
* %RAX Accessing cpu registers in watch expression (only full registers, not yet AH or AL or EAX on 64bit) <br />
* Intrinsic functions: [[FpDebug-Watches-Intrinsic-Functions]]<br />
* Intrinsic/extended operators: MyArray[1..3] array slice with operator mapping. [[FpDebug-Watches-Intrinsic-Functions#Intrinsic_Operators]]<br />
* Option to detect "variant" and call "SysVarToLStr" in the target app.<br />
* Suspend/Resume individual threads (must be done while app is paused, and will be applied for subsequent step/run)<br />
* Partial improvements to debug in DLL: https://wiki.freepascal.org/Debugger_Status#Other<br />
* Disassembler now annotates lines for call/jmp/jne/... with info on the target address (function name, file, line)<br />
* F7/F8 Step-Into/Over can now be used to start the debugger and run to the first line of the main program begin/end.<br />
<br />
==== LazDebuggerFpLldb (default on MacOS) ====<br />
<br />
* Added Mem-Limits (and String,Pchar,Array) to the debugger config<br />
: See https://wiki.freepascal.org/LazDebuggerFp (MaxMemReadSize, MaxStringLen, MaxArrayLen, MaxNullStringSearchLen)<br />
: Limiting the default results for watches/locals/stack-params,... can prevent slow evaluation.<br />
: Arrays can then be browsed in the watches window, using the new "paged browser for arrays" (expand via [+])<br />
<br />
=== Floating point properties in the Object Inspector ===<br />
* The Object Inspector now explicitely disallows to set a floating point property's value to +/-Inf or NaN.<br />
* Reason: whilst +/-Inf and NaN are valid values for a floating point property, they cannot be streamed (so, the form could not be loaded) and setting it to NaN caused havoc in the IDE.<br />
* Remedy: set the value at runtime (in code)<br />
<br />
=== Reading unit names in lfm ===<br />
<br />
FPC 3.3.1 component writer supports optionally writing types with their unit names as ''unitname/type''. Lazarus can now read this.<br />
<br />
=== Ambiguous Classtypes ===<br />
<br />
You can now register two component classes with the same name, e.g. ''fresnel.TButton'' and ''StdCtrls.TButton''. You can even put both on the same form.<br />
<br />
=== Editor ===<br />
<br />
- Highlight for PasDoc<br />
<br />
=== IDE Options ===<br />
<br />
In Tools -> Options -> Environment -> Window page these three settings are now <b>ON</b> by default :<br />
* IDE title starts with project name<br />
* IDE title shows project directory<br />
* IDE title shows selected build mode<br />
It has been requested by many users. If the IDE's title bar has no info about the active project, a user must open Project Inspector or Project Options to see it, which is inconvenient.<br />
<br />
=== Lazarus Examples ===<br />
A new approach to Examples that copies an Example to a fresh working area (avoiding the Linux Read Only Problem) and, possibly an easier to use search model.<br />
<br />
== IDE Interface Changes ==<br />
<br />
== Components ==<br />
=== TAChart ===<br />
* The '''TLegendClickTools''' now is able to detect clicks on series legend items and reports the clicked series in the new '''OnSeriesClick''' event.<br />
* New '''TDatapointMarksClickTool''' which becomes active when the user clicked on the marks of a series.<br />
* New property '''TickWidth''' for the chart axes.<br />
* New property '''FullWidth''' for the chart title and footer to run their background across the entire chart width.<br />
* New property '''RandomColors''' for <tt>TRandomChartSource</tt>.<br />
* New properties '''YIndexWhiskerMin''', '''YIndexBarMin''', '''YIndexCenter''', '''YIndexBarMax''', '''YIndexWhiskerMax''', and '''YDataLayout''' in TBoxAndWhiskerSeries for more flexible assignment of y values to the parts of the box/whisher shape.<br />
* New option '''aipInteger''' in the set TAxisIntervalParamOptions which sets axis labels only at integer values and thus supresses the unwanted intermediate labels in bar charts and helps to enforce labels in logarithmic plots at the powers of the logarithmic base (usually 10).<br />
* New event '''OnAddStyleToLegend''' for TChartStyles. It has a boolean parameter <tt>AddToLegend</tt> with which you can determine whether the series level using this style is displayed in the legend.<br />
<br />
=== TDateTimePicker ===<br />
* New properties MonthDisplay and CustomMonthNames. They are meant to replace the MonthNames property, which has been deprecated.<br />
* New property DecimalSeparator. Allows a user-specified value to be used instead of a hard-coded Colon character.<br />
<br />
=== TDBDateTimePicker ===<br />
* Adds Options as a published property.<br />
* Publishes the DecimalSeparator property.<br />
* Publishes the missing Alignment property. Consistent with TDateTimePicker,<br />
<br />
=== T(Float)SpinEditEx ===<br />
* New property '''Orientation''' which allows to arrange the spin buttons horizontally.<br />
<br />
=== TCheckListBox ===<br />
* Adds HeaderColor and HeaderBackgroundColor properties. Used on list items where the Header property is enabled. Implemented for the Win32 widget set.<br />
<br />
=== Pas2js ===<br />
* lazbuild now can compile pas2js projects by passing the environment variable PAS2JS with the path of the pas2js executable.<br />
* Project groups with pas2js projects now can compile without being opened.<br />
* New project type [[lazarus_pas2js_integration#Progressive_Web_Application|Progressive Web Application]]<br />
* New project type [[lazarus_pas2js_integration#Electron_Web_Application|Electron Web Application]]<br />
* pas2jsdsgn now uses the SimpleWebServerGUI package, replacing its own http server controller.<br />
* F9, Run now builds, starts a HTTP server and a browser<br />
<br />
=== Lazarus Icon Collection ===<br />
* Not a component, but the Lazarus installation now contains a folder with general-purpose icons for usage in toolbars, menus, buttons etc. of any GUI applications (folder ''images/general_purpose'').<br />
* The images come in various sizes and thus are compatible with the scaled image list of Lazarus v2.0+.<br />
* Author: Roland Hahn (https://www.rhsoft.de/).<br />
* License: Creative Commons CC0 (no restrictions in usage).<br />
<br />
=== gir2pascal ===<br />
<br />
* Sources of [[gir2pascal]] (a tool to convert [https://gi.readthedocs.io/ GObject Introspection] descriptions to Pascal files) are now included in Lazarus source tree ([https://gitlab.com/freepascal.org/lazarus/lazarus/-/tree/main/tools/gir2pascal tools/gir2pascal] directory) and maintained there.<br />
<br />
=== lazdelphi ===<br />
<br />
An IDE addon adding a parser for the Delphi compiler errors and hints. You can run dcc32.exe as external tool or '''execute before''' command in the compiler options and use the ''Delphi Compiler'' parser for the output, so that the errors/hints in the Messages window can open the source. See [[Lazarus Delphi Compiler Tool]]<br />
<br />
=== Jedi Code Format ===<br />
* Some improvements in code formatting.<br />
* The jcf command line tool is now a text-mode application and no longer requires XWindow on linux to run.<br />
<br />
=== DockedFormEditor ===<br />
<br />
In case you are still using the sparta docked form editor, use the '''dockedformeditor''' package instead.<br />
<br />
== Changes affecting compatibility ==<br />
<br />
===IDE Settings===<br />
<br />
====Run > Run Parameters====<br />
The Working-Dir and the Launch-/Host-App can now be specified relative to the Project-Dir.<br />
As a result of this, and due to inconsistencies between "Run in debugger" and "Run without debug" some details of how those fields are resolved changed.<br />
<br />
In some cases you may therefore have to adjust your settings.<br />
<br />
;The Working-dir is now determined as follows:<br />
<br />
# BuildMode.RunParams "working directory" set by user (New, this can now be relative to project dir)<br />
# Project Dir, if not virtual<br />
# Directory from Host-App (RunParams), or if (and only if) Host-App is empty from Project.exe (If host app is in %PATH, then there is no "working directory")<br />
<br />
The first 2 steps are the same as before the change.<br />
The 3rd step was previously only used for "debugging", but "run without debug" did use: "Launch-App", "Host-App", Project.exe<br />
<br />
;Change: The path of the "Launch App" is no longer considered<br />
<br />
; The Launch-/Host-App location are now determined as follows:<br />
# An app with absolute path is used as given<br />
# A relative path (including no path at all) is resolved as relative to the Project-dir.<br />
# An app without any path at all (if not found in step 2) is searched in the %PATH environment.<br />
<br />
;Change: Search in %PATH was only done by "run without debug", but not by debug. It is now done by both.<br />
;Change: Checking for an exe relative to the project-dir was added.<br />
<br />
<br />
===LCL incompatibility===<br />
====TLabel: autosized and right-aligned====<br />
*Old behaviour: Autosized label with Alignment=taRightJustify but Anchors=[akLeft,...] grew to left.<br />
*New behaviour: The label grows to right now.<br />
*Reason: It wasn't possible to implement the behavior also for hidden labels without significant extensions in the LCL. The LCL has a different and more generic feature of control-based anchoring that delivers the same effect (see Remedy down), so it is not needed and wanted to double this feature and make the LCL code more complex and prone to bugs.<br />
*Remedy: Use the LCL anchoring to a secondary control. Anchor the right side of the label to another control. Then the autosized label will grow to the left but won't move to the right when the parent is resized like it is done with a simple akRight anchor without a reference control.<br />
<br />
====TDateEdit/TTimeEdit====<br />
The value of NullDate has changed.<br><br />
Reason:<br />
* It was impossible to actually select the date corresponding to NullDate (30 dec 1899 by default) in the control.<br />
* Remedy (1): if your code depended on NullDate actually being 0.0, you have to adjust your code.<br />
* Remedy (2): if your code used NullDate for a TTimeEdit, change that to the new constant NullTime instead.<br />
* Note: NullDate is actually a writeable constant. This was kept for compatibility reasons. It is however a bad idea to change it's value to anything that is an actual date that is within the range of the control.<br />
<br />
====Cocoa====<br />
Some global configuration variables were moved from CocoaInt to CocoaConfig. such as CocoaBasePPI, CocoaIconUse, CocoaToggleBezel, CocoaToggleType etc.<br />
<br />
====GTK3====<br />
GTK3 is no longer supported on earlier Linuxes, such as Ubuntu 20.04. It requires GTK >= 3.24.24 and Glib2.0 >= 2.66<br />
<br />
===LazUtils===<br />
<br />
==== Masks unit ====<br />
The masks unit has been completely rewritten.<br><br />
Reasons:<br />
* speed: the old Matches() method had O(n^2) or even O(n^3) characteristics.<br />
* improved control over how the mask is interpreted.<br />
New types (for parameters) and a dedicated TMaskWindows class have been added.<br><br />
TMask.MatchesWindowsMask and the old TMaskOptions type have been deprecated and will be removed in the next release.<br><br />
===== Ranges and Sets =====<br />
* The old masks implementation supported sets, but not ranges. The new implementation supports both sets ([abc]) and ranges ([a-c]). As a consequence a '-' inside such a construct is now interpreted as part of the range definition, not as a literal '-'.<br />
* Reason: ranges are a good thing to have by default (the old implementation simply lacked this). We decided it's a small price to pay.<br />
* Remedy: either escape the '-' with EscapeChar (which defaults to '\') or exclude mocRange from the TMaskOpcodes parameter.<br />
<br />
===== Constructors do not fail anymore on an invalid mask =====<br />
* When providing an invalid mask to the old T(Windows)Mask(List) constructors an exception was raised.<br />
* The new constructors do not raise an exception in this case. Instead an exception is raised in when Matches() is called.<br />
* Reason: it's not very nice to have a constructor fail.<br />
<br />
==== Translations unit ====<br />
Added GetLanguageID function. It returns a record with language code (in ISO 639-1 or ISO 639-2) and country code (in ISO 3166) for current system locale.<br><br />
Added GetLanguageIDFromLocaleName function. It parses Unix locale name and returns a record with language code and country code. It is useful to parse language identifiers passed e. g. via command-line parameters.<br />
<br />
Implementation is based on GetLanguageIDs procedure from GetText unit, but is rewritten to have the following properties:<br />
* Language and country codes are always returned in ISO formats on Windows.<br />
* Unix locale identifier is properly parsed and language/country codes are properly extracted.<br />
* Don't assume that language code is always two-letter (ISO 639-1), it can have bigger length (e. g. three letters, like in ISO 639-2).<br />
* Locale ID is returned in a record type. This will allow to return additional fields in backwards-compatible manner in future. Currently it contains language code, country code and language ID (combination of language code and country code).<br />
<br />
These functions are used now throughout the Lazarus codebase. This greatly improves automatic language detection and loading of correct translations by Lazarus:<br />
* Three-letter (ISO 639-2) language identifiers are no more truncated to two letters. Thus, translations for such languages will be correctly loaded when available.<br />
* Previously on Windows some language and country codes were obtained in non-ISO format, which prevented correct loading of some translations, e. g. Chinese (zh_CN).<br />
* On Unix translations with country codes, like Brazilian Portuguese (pt_BR) or Chinese (zh_CN) are correctly loaded now.<br />
* macOS is now handled as any other Unix. This removes dependency on language list in Lazarus bundle (which had to be maintained manually) and thus fixes loading of Czech, Hungarian, Brazilian Portuguese, Ukrainian translations.<br />
<br />
==== LazUTF8 unit ====<br />
* Deprecated LazGetLanguageIDs (returns combination of language and country codes) and LazGetShortLanguageID (returns only language code) procedures.<br />
* Reason: this functionality belongs to Translations unit (calls of these procedures are almost always followed by calls to procedures from Translations unit), and these procedures are now thin wrappers of GetLanguageID function from Translations unit.<br />
* Remedy: use GetLanguageID function from Translations unit.<br />
<br />
==== LazUTF8Classes and LazUTF8SysUtils units ====<br />
Everything in these units was deprecated for a long time and now they were removed.<br />
LazUTF8SysUtils was earlier renamed to LazSysUtils and this deprecated version was left for a transit period.<br />
* Class TStringListUTF8 can be replaced with TStringList.<br />
* Class TMemoryStreamUTF8 can be replaced with TMemoryStream.<br />
* Global procedure LoadStringsFromFileUTF8 can be replaced with TStrings.LoadFromFile.<br />
* Global procedure SaveStringsToFileUTF8 can be replaced with TStrings.SaveToFile.<br />
* Functions NowUTC and GetTickCount64 can be found in LazSysUtils.<br />
<br />
===Components incompatibility===<br />
====LazControls====<br />
======TSpinEditExBase derived classes======<br />
* All derived classes form TSpinEditExBase must implement a SameValue method. This method is defined as an <b>abstract</b> method in TSpinEditExBase. <br />
* Reason: All derived classes used Math.SameValue. This is wrong for comparing integer types (even if is is safe when comparing relative small values).<br />
* Remedy: unfortunately you'll have to adjust your code.<br />
======TFloatSpinEditEx======<br />
* The property NumbersOnly is no longer published.<br />
* Reason: the property makes no sense for this control and only confuses users.<br />
* Remedy: if you really need NumbersOnly to be True, you must set it in code.<br />
====FpVectorial====<br />
* The <tt>Size</tt> element in the FPVectorial <tt>TvFont</tt> record is a floating point value now (type <tt>double</tt>).<br />
* Reason: Avoid rounding errors because the drawing coordinates are <tt>double</tt> already.<br />
* Remedy: There is rarely a chance that this change will have an effect on user code. Only when the font size is stored in a variable it must be declared as <tt>double</tt> rather than as <tt>integer</tt>.<br />
====TurboPower_ipro====<br />
* Type declarations and the html nodes were moved from unit IpHtml to separate units, IpHtmlTypes, IpHtmlClasses and IpHtmlNodes. This may break compilation of existing projects.<br />
* Reason: Improve maintainability of the extremely long unit IpHtml<br />
* Remedy: Add IpHtmlTypes, IpHtmlClasses and/or IpHtmlNodes to the uses clause of the project unit(s) when an "identifier not found" error referring to this package is reported by the compiler.<br />
<br />
== Other release notes ==<br />
<br />
{{Navbar Lazarus Release Notes}}<br />
<br />
[[Category:Release Notes]]<br />
[[Category:Lazarus]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Lazarus_3.0_release_notes&diff=157936Lazarus 3.0 release notes2023-12-21T23:51:36Z<p>Dbannon: mention new Examples Model</p>
<hr />
<div><br />
== LCL Interfaces Changes ==<br />
<br />
=== Cocoa ===<br />
* IME fully supported, such as Chinese/Japanese/Korean, DeadKeys, Emoji & Symbols.<br />
* Multi Displays/Monitors fully supported.<br />
* Docking fully supported, including the IDE.<br />
* Cursor completely refactored and significantly improved, compatible with macOS Ventura.<br />
* TPageControl significantly improved.<br />
* Many controls improved (TComboxBox/TListBox/TDateTimePicker/TStaticText/TSpeedButton/TPanel etc.)<br />
* Many memory leaks fixed.<br />
<br />
=== Qt ===<br />
* Implemented TCheckBox.Alignment and TRadioButton.Alignment.<br />
* Implemented TCustomComboBox.AdjustDropDown and TCustomComboBox.ItemWidth.<br />
<br />
=== Qt5 ===<br />
* Qt5 uses native event loop on all platforms. C bindings are updated. Minimum C bindings version for lazarus 3.0 is 1.2.15.<br />
* Note that most Linux Distributions will not have an appropriate libqt5pas library until their next release after the formal release of Lazarus 3.0. Build your own from the Lazarus source tree or download from https://github.com/davidbannon/libqt5pas/releases/latest<br />
* Implemented TCheckBox.Alignment and TRadioButton.Alignment.<br />
* Implemented TCustomComboBox.AdjustDropDown and TCustomComboBox.ItemWidth.<br />
<br />
=== Qt6 ===<br />
* Qt6 widgetset implemented. C bindings are based on Qt6 6.2.0 LTS. Minimum C bindings version for lazarus 3.0 is 6.2.7.<br />
* Note that most Linux Distributions will not have an appropriate libqt6pas library until their next release after the formal release of Lazarus 3.0. Build your own from the Lazarus source tree or download from https://github.com/davidbannon/libqt6pas/releases/latest<br />
* Implemented TCheckBox.Alignment and TRadioButton.Alignment.<br />
* Implemented TCustomComboBox.AdjustDropDown and TCustomComboBox.ItemWidth.<br />
<br />
=== Gtk3 ===<br />
* Gtk3 pascal bindings are completely reworked.<br />
* A number of stability improvements.<br />
* Now requires GTK >= 3.24.24 and Glib2.0 >= 2.66<br />
<br />
== LCL Changes ==<br />
=== TCustomImageList ===<br />
TCustomImageList made more extensible:<br />
# Protected MarkAsChanged method is added, which sets FChanged to true (this allows to make custom triggers for OnChange event).<br />
# Virtual protected DoAfterUpdateStarted and DoBeforeUpdateEnded methods are added. They are called in first BeginUpdate and last EndUpdate respectively.<br />
<br />
=== TTaskDialog ===<br />
*Old behaviour Win32: A placeholder icon was used for FooterIcon = tdiNone and MainIcon = tdiNone.<br />
*New behaviour Win32: No icon is used for FooterIcon = tdiNone and MainIcon = tdiNone.<br />
*Reason: Removing drawing glitch. The text move over to allow more content and better alignment. See {{MantisLink|39172}}<br />
<br />
=== TSpeedButton ===<br />
*Old behaviour: Multi-line captions could only be entered by code. And multi-line captions were always left-aligned.<br />
*New behaviour: Multi-line captions can also be entered in the object inspector. New property Alignment to specify whether the caption should be left-/right-aligned or centered. Default: centered, like in Delphi.<br />
*Reason: Better usability.<br />
<br />
=== TLabel.Transparent, .Color and .ParentColor changes ===<br />
*Old behaviour: Transparent property was bound to Color=clNone<br />
*New behaviour: Transparent is a standalone property<br />
*Reason: Delphi compatibility and to fix ParentColor issues.<br />
*Remedy: If you are setting the Color property, Transparent is not automatically switched from True to False now, you have to do it yourself. This is in compliance with Delphi and also solves problems with Color/ParentColor changes.<br />
<br />
=== TPanel.VerticalAlignment ===<br />
*Old behaviour: The panel caption was always centered vertically.<br />
*New behaviour: The new property VerticalAlignment (taAlignTop, taAlignBottom, taVerticalCenter) allows to place the caption also at the top or bottom of the panel interior.<br />
*Reason: Delphi compatibility and better usability<br />
<br />
=== TCalendar ===<br />
* Properties MinDate and MaxDate are implemented. These limits are only imposed if MaxDate > MinDate. Unfortunately GTK2/3 widgetsets do not support this, so selecting a date outside the MinDate/MaxDate range will still be possible there.<br />
<br />
=== TCheckbox, TRadioButton ===<br />
*Different calculation of checkbox/radiobutton size in order to correctly take care of Win-10 "ease of access" feature. See [https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/39398 issue #39398]<br />
*Consequence: lfm files will contain different sizes of these controls (if auto-sized) compared with earlier versions.<br />
<br />
=== Grids ===<br />
* You can now set the cell editors properties ParentColor and ParentFont by including goEditorParentColor resp. goEditorParentFont in the grid's Options2 property.<br />
<br />
=== TShellTreeView ===<br />
* New property <tt>ExpandCollapseMode</tt> defining options whether a collapsing node should clear its child nodes.<br />
* Implements custom sorting of the treeview items by setting <tt>FileSortType</tt> to <tt>fstCustom</tt> and providing a custom compare function in the event <tt>OnSortCompare</tt>.<br />
<br />
=== TShellListView ===<br />
* TShellListView now subclasses TListItem, so it can store file info in it. <br />
* If you use OnCreateItemClass to create your own descendant of TListItem, it may be advisable to base your own class on TShellListItem instead of on TListItem. This way you will also have access to the TShellListItem's FileInfo property.<br />
<br />
=== TTreeView ===<br />
* Adds ShowSeparators as a published property like ShowLines and ShowRoot.<br />
<br />
== IDE Changes ==<br />
=== Character map ===<br />
* Resizable characters to improve readability.<br />
* The character map was split off from the IDE and moved to separate packages. The designtime package is installed by default so that there is no difference in the IDE. Now users can access it in their own applications after adding the runtime package "charactermappkg.lpk" to the project requirements.<br />
<br />
=== Debugger ===<br />
<br />
==== Project Options ====<br />
<br />
* "Run(F9)" can either be "Debug" or "Run without debug".<br />
: In newly created Debug/Release modes, the Release mode will no longer invoke the debugger by default.<br />
: Existing Debug/Release modes must be edited, to enable this.<br />
* Per project "Debugger backend" settings. In addition to choosing a specific backend from the global IDE settings, a backend can be configured just for the project (i.e. special gdb-server settings)<br />
<br />
==== IDE Dialogs ====<br />
<br />
* Improved <u>Watches window</u><br />
** Expand/Unfold for classes, records, etc<br />
** Expand/Unfold with paged browser for arrays<br />
** Drag and Drop to reorder watches<br />
** Drag and Drop to create new "top level" watches from nested entries (in expand/unfolded lists)<br />
** Address column for types with internal pointer (classes, long-string, dyn-array, (real) pointer)<br />
* Improved <u>Locals window</u><br />
** Expand/Unfold for classes, records, etc<br />
** Expand/Unfold with paged browser for arrays<br />
** Address column for types with internal pointer (classes, long-string, dyn-array, (real) pointer)<br />
** Power button<br />
* Improved <u>Inspect window</u><br />
** Fixed: Updating value when context changes<br />
** Added options for Function calling / and "Converter" (See FpDebug: SysVarToLstr)<br />
** Added filter to search for text in name or value.<br />
** Added ctrl-Up/Down/Page-Up/Page-Down to navigate the grid.<br/>Alt-Left/Right for history. And ctrl-Enter to select.<br />
* Improved <u>Evaluate/Modify window</u><br />
** New Layout<br />
** Added DisplayFormat<br />
** Added options for Function calling / and "Converter" (See FpDebug: SysVarToLstr)<br />
* Improved <u>Assembler window</u><br />
** Added history navigation (forward/backward)<br />
** For FpDebug: added annotations to jump/call targets, and allow to ctrl-click to disassemble target address<br />
<br />
==== FpDebug / LazDebuggerFp ====<br />
<br />
* Improved "function calling" in watch eval. See [[FpDebug-Watches-FunctionEval]]<br />
* %RAX Accessing cpu registers in watch expression (only full registers, not yet AH or AL or EAX on 64bit) <br />
* Intrinsic functions: [[FpDebug-Watches-Intrinsic-Functions]]<br />
* Intrinsic/extended operators: MyArray[1..3] array slice with operator mapping. [[FpDebug-Watches-Intrinsic-Functions#Intrinsic_Operators]]<br />
* Option to detect "variant" and call "SysVarToLStr" in the target app.<br />
* Suspend/Resume individual threads (must be done while app is paused, and will be applied for subsequent step/run)<br />
* Partial improvements to debug in DLL: https://wiki.freepascal.org/Debugger_Status#Other<br />
* Disassembler now annotates lines for call/jmp/jne/... with info on the target address (function name, file, line)<br />
* F7/F8 Step-Into/Over can now be used to start the debugger and run to the first line of the main program begin/end.<br />
<br />
==== LazDebuggerFpLldb (default on MacOS) ====<br />
<br />
* Added Mem-Limits (and String,Pchar,Array) to the debugger config<br />
: See https://wiki.freepascal.org/LazDebuggerFp (MaxMemReadSize, MaxStringLen, MaxArrayLen, MaxNullStringSearchLen)<br />
: Limiting the default results for watches/locals/stack-params,... can prevent slow evaluation.<br />
: Arrays can then be browsed in the watches window, using the new "paged browser for arrays" (expand via [+])<br />
<br />
=== Floating point properties in the Object Inspector ===<br />
* The Object Inspector now explicitely disallows to set a floating point property's value to +/-Inf or NaN.<br />
* Reason: whilst +/-Inf and NaN are valid values for a floating point property, they cannot be streamed (so, the form could not be loaded) and setting it to NaN caused havoc in the IDE.<br />
* Remedy: set the value at runtime (in code)<br />
<br />
=== Reading unit names in lfm ===<br />
<br />
FPC 3.3.1 component writer supports optionally writing types with their unit names as ''unitname/type''. Lazarus can now read this.<br />
<br />
=== Ambiguous Classtypes ===<br />
<br />
You can now register two component classes with the same name, e.g. ''fresnel.TButton'' and ''StdCtrls.TButton''. You can even put both on the same form.<br />
<br />
=== Editor ===<br />
<br />
- Highlight for PasDoc<br />
<br />
=== IDE Options ===<br />
<br />
In Tools -> Options -> Environment -> Window page these three settings are now <b>ON</b> by default :<br />
* IDE title starts with project name<br />
* IDE title shows project directory<br />
* IDE title shows selected build mode<br />
It has been requested by many users. If the IDE's title bar has no info about the active project, a user must open Project Inspector or Project Options to see it, which is inconvenient.<br />
<br />
=== Lazarus Examples ===<br />
A new approach to Examples that copies an Example to a fresh working area (avoiding the Linux Read Only Problem) and, possibly an easier to use search model.<br />
<br />
== IDE Interface Changes ==<br />
<br />
== Components ==<br />
=== TAChart ===<br />
* The '''TLegendClickTools''' now is able to detect clicks on series legend items and reports the clicked series in the new '''OnSeriesClick''' event.<br />
* New '''TDatapointMarksClickTool''' which becomes active when the user clicked on the marks of a series.<br />
* New property '''TickWidth''' for the chart axes.<br />
* New property '''FullWidth''' for the chart title and footer to run their background across the entire chart width.<br />
* New property '''RandomColors''' for <tt>TRandomChartSource</tt>.<br />
* New properties '''YIndexWhiskerMin''', '''YIndexBarMin''', '''YIndexCenter''', '''YIndexBarMax''', '''YIndexWhiskerMax''', and '''YDataLayout''' in TBoxAndWhiskerSeries for more flexible assignment of y values to the parts of the box/whisher shape.<br />
* New option '''aipInteger''' in the set TAxisIntervalParamOptions which sets axis labels only at integer values and thus supresses the unwanted intermediate labels in bar charts and helps to enforce labels in logarithmic plots at the powers of the logarithmic base (usually 10).<br />
* New event '''OnAddStyleToLegend''' for TChartStyles. It has a boolean parameter <tt>AddToLegend</tt> with which you can determine whether the series level using this style is displayed in the legend.<br />
<br />
=== TDateTimePicker ===<br />
* New properties MonthDisplay and CustomMonthNames. They are meant to replace the MonthNames property, which has been deprecated.<br />
* New property DecimalSeparator. Allows a user-specified value to be used instead of a hard-coded Colon character.<br />
<br />
=== TDBDateTimePicker ===<br />
* Adds Options as a published property.<br />
* Publishes the DecimalSeparator property.<br />
* Publishes the missing Alignment property. Consistent with TDateTimePicker,<br />
<br />
=== T(Float)SpinEditEx ===<br />
* New property '''Orientation''' which allows to arrange the spin buttons horizontally.<br />
<br />
=== TCheckListBox ===<br />
* Adds HeaderColor and HeaderBackgroundColor properties. Used on list items where the Header property is enabled. Implemented for the Win32 widget set.<br />
<br />
=== Pas2js ===<br />
* lazbuild now can compile pas2js projects by passing the environment variable PAS2JS with the path of the pas2js executable.<br />
* Project groups with pas2js projects now can compile without being opened.<br />
* New project type [[lazarus_pas2js_integration#Progressive_Web_Application|Progressive Web Application]]<br />
* New project type [[lazarus_pas2js_integration#Electron_Web_Application|Electron Web Application]]<br />
* pas2jsdsgn now uses the SimpleWebServerGUI package, replacing its own http server controller.<br />
* F9, Run now builds, starts a HTTP server and a browser<br />
<br />
=== Lazarus Icon Collection ===<br />
* Not a component, but the Lazarus installation now contains a folder with general-purpose icons for usage in toolbars, menus, buttons etc. of any GUI applications (folder ''images/general_purpose'').<br />
* The images come in various sizes and thus are compatible with the scaled image list of Lazarus v2.0+.<br />
* Author: Roland Hahn (https://www.rhsoft.de/).<br />
* License: Creative Commons CC0 (no restrictions in usage).<br />
<br />
=== gir2pascal ===<br />
<br />
* Sources of [[gir2pascal]] (a tool to convert [https://gi.readthedocs.io/ GObject Introspection] descriptions to Pascal files) are now included in Lazarus source tree ([https://gitlab.com/freepascal.org/lazarus/lazarus/-/tree/main/tools/gir2pascal tools/gir2pascal] directory) and maintained there.<br />
<br />
=== lazdelphi ===<br />
<br />
An IDE addon adding a parser for the Delphi compiler errors and hints. You can run dcc32.exe as external tool or '''execute before''' command in the compiler options and use the ''Delphi Compiler'' parser for the output, so that the errors/hints in the Messages window can open the source. See [[Lazarus Delphi Compiler Tool]]<br />
<br />
=== Jedi Code Format ===<br />
* Some improvements in code formatting.<br />
* The jcf command line tool is now a text-mode application and no longer requires XWindow on linux to run.<br />
<br />
=== DockedFormEditor ===<br />
<br />
In case you are still using the sparta docked form editor, use the '''dockedformeditor''' package instead.<br />
<br />
== Changes affecting compatibility ==<br />
<br />
===IDE Settings===<br />
<br />
====Run > Run Parameters====<br />
The Working-Dir and the Launch-/Host-App can now be specified relative to the Project-Dir.<br />
As a result of this, and due to inconsistencies between "Run in debugger" and "Run without debug" some details of how those fields are resolved changed.<br />
<br />
In some cases you may therefore have to adjust your settings.<br />
<br />
;The Working-dir is now determined as follows:<br />
<br />
# BuildMode.RunParams "working directory" set by user (New, this can now be relative to project dir)<br />
# Project Dir, if not virtual<br />
# Directory from Host-App (RunParams), or if (and only if) Host-App is empty from Project.exe (If host app is in %PATH, then there is no "working directory")<br />
<br />
The first 2 steps are the same as before the change.<br />
The 3rd step was previously only used for "debugging", but "run without debug" did use: "Launch-App", "Host-App", Project.exe<br />
<br />
;Change: The path of the "Launch App" is no longer considered<br />
<br />
; The Launch-/Host-App location are now determined as follows:<br />
# An app with absolute path is used as given<br />
# A relative path (including no path at all) is resolved as relative to the Project-dir.<br />
# An app without any path at all (if not found in step 2) is searched in the %PATH environment.<br />
<br />
;Change: Search in %PATH was only done by "run without debug", but not by debug. It is now done by both.<br />
;Change: Checking for an exe relative to the project-dir was added.<br />
<br />
<br />
===LCL incompatibility===<br />
====TLabel: autosized and right-aligned====<br />
*Old behaviour: Autosized label with Alignment=taRightJustify but Anchors=[akLeft,...] grew to left.<br />
*New behaviour: The label grows to right now.<br />
*Reason: It wasn't possible to implement the behavior also for hidden labels without significant extensions in the LCL. The LCL has a different and more generic feature of control-based anchoring that delivers the same effect (see Remedy down), so it is not needed and wanted to double this feature and make the LCL code more complex and prone to bugs.<br />
*Remedy: Use the LCL anchoring to a secondary control. Anchor the right side of the label to another control. Then the autosized label will grow to the left but won't move to the right when the parent is resized like it is done with a simple akRight anchor without a reference control.<br />
<br />
====TDateEdit/TTimeEdit====<br />
The value of NullDate has changed.<br><br />
Reason:<br />
* It was impossible to actually select the date corresponding to NullDate (30 dec 1899 by default) in the control.<br />
* Remedy (1): if your code depended on NullDate actually being 0.0, you have to adjust your code.<br />
* Remedy (2): if your code used NullDate for a TTimeEdit, change that to the new constant NullTime instead.<br />
* Note: NullDate is actually a writeable constant. This was kept for compatibility reasons. It is however a bad idea to change it's value to anything that is an actual date that is within the range of the control.<br />
<br />
====Cocoa====<br />
Some global configuration variables were moved from CocoaInt to CocoaConfig. such as CocoaBasePPI, CocoaIconUse, CocoaToggleBezel, CocoaToggleType etc.<br />
<br />
====GTK3====<br />
GTK3 is no longer supported on earlier Linuxes, such as Ubuntu 20.04. It requires GTK >= 3.24.24 and Glib2.0 >= 2.66<br />
<br />
===LazUtils===<br />
<br />
==== Masks unit ====<br />
The masks unit has been completely rewritten.<br><br />
Reasons:<br />
* speed: the old Matches() method had O(n^2) or even O(n^3) characteristics.<br />
* improved control over how the mask is interpreted.<br />
New types (for parameters) and a dedicated TMaskWindows class have been added.<br><br />
TMask.MatchesWindowsMask and the old TMaskOptions type have been deprecated and will be removed in the next release.<br><br />
===== Ranges and Sets =====<br />
* The old masks implementation supported sets, but not ranges. The new implementation supports both sets ([abc]) and ranges ([a-c]). As a consequence a '-' inside such a construct is now interpreted as part of the range definition, not as a literal '-'.<br />
* Reason: ranges are a good thing to have by default (the old implementation simply lacked this). We decided it's a small price to pay.<br />
* Remedy: either escape the '-' with EscapeChar (which defaults to '\') or exclude mocRange from the TMaskOpcodes parameter.<br />
<br />
===== Constructors do not fail anymore on an invalid mask =====<br />
* When providing an invalid mask to the old T(Windows)Mask(List) constructors an exception was raised.<br />
* The new constructors do not raise an exception in this case. Instead an exception is raised in when Matches() is called.<br />
* Reason: it's not very nice to have a constructor fail.<br />
<br />
==== Translations unit ====<br />
Added GetLanguageID function. It returns a record with language code (in ISO 639-1 or ISO 639-2) and country code (in ISO 3166) for current system locale.<br><br />
Added GetLanguageIDFromLocaleName function. It parses Unix locale name and returns a record with language code and country code. It is useful to parse language identifiers passed e. g. via command-line parameters.<br />
<br />
Implementation is based on GetLanguageIDs procedure from GetText unit, but is rewritten to have the following properties:<br />
* Language and country codes are always returned in ISO formats on Windows.<br />
* Unix locale identifier is properly parsed and language/country codes are properly extracted.<br />
* Don't assume that language code is always two-letter (ISO 639-1), it can have bigger length (e. g. three letters, like in ISO 639-2).<br />
* Locale ID is returned in a record type. This will allow to return additional fields in backwards-compatible manner in future. Currently it contains language code, country code and language ID (combination of language code and country code).<br />
<br />
These functions are used now throughout the Lazarus codebase. This greatly improves automatic language detection and loading of correct translations by Lazarus:<br />
* Three-letter (ISO 639-2) language identifiers are no more truncated to two letters. Thus, translations for such languages will be correctly loaded when available.<br />
* Previously on Windows some language and country codes were obtained in non-ISO format, which prevented correct loading of some translations, e. g. Chinese (zh_CN).<br />
* On Unix translations with country codes, like Brazilian Portuguese (pt_BR) or Chinese (zh_CN) are correctly loaded now.<br />
* macOS is now handled as any other Unix. This removes dependency on language list in Lazarus bundle (which had to be maintained manually) and thus fixes loading of Czech, Hungarian, Brazilian Portuguese, Ukrainian translations.<br />
<br />
==== LazUTF8 unit ====<br />
* Deprecated LazGetLanguageIDs (returns combination of language and country codes) and LazGetShortLanguageID (returns only language code) procedures.<br />
* Reason: this functionality belongs to Translations unit (calls of these procedures are almost always followed by calls to procedures from Translations unit), and these procedures are now thin wrappers of GetLanguageID function from Translations unit.<br />
* Remedy: use GetLanguageID function from Translations unit.<br />
<br />
==== LazUTF8Classes and LazUTF8SysUtils units ====<br />
Everything in these units was deprecated for a long time and now they were removed.<br />
LazUTF8SysUtils was earlier renamed to LazSysUtils and this deprecated version was left for a transit period.<br />
* Class TStringListUTF8 can be replaced with TStringList.<br />
* Class TMemoryStreamUTF8 can be replaced with TMemoryStream.<br />
* Global procedure LoadStringsFromFileUTF8 can be replaced with TStrings.LoadFromFile.<br />
* Global procedure SaveStringsToFileUTF8 can be replaced with TStrings.SaveToFile.<br />
* Functions NowUTC and GetTickCount64 can be found in LazSysUtils.<br />
<br />
===Components incompatibility===<br />
====LazControls====<br />
======TSpinEditExBase derived classes======<br />
* All derived classes form TSpinEditExBase must implement a SameValue method. This method is defined as an <b>abstract</b> method in TSpinEditExBase. <br />
* Reason: All derived classes used Math.SameValue. This is wrong for comparing integer types (even if is is safe when comparing relative small values).<br />
* Remedy: unfortunately you'll have to adjust your code.<br />
======TFloatSpinEditEx======<br />
* The property NumbersOnly is no longer published.<br />
* Reason: the property makes no sense for this control and only confuses users.<br />
* Remedy: if you really need NumbersOnly to be True, you must set it in code.<br />
====FpVectorial====<br />
* The <tt>Size</tt> element in the FPVectorial <tt>TvFont</tt> record is a floating point value now (type <tt>double</tt>).<br />
* Reason: Avoid rounding errors because the drawing coordinates are <tt>double</tt> already.<br />
* Remedy: There is rarely a chance that this change will have an effect on user code. Only when the font size is stored in a variable it must be declared as <tt>double</tt> rather than as <tt>integer</tt>.<br />
====TurboPower_ipro====<br />
* Type declarations and the html nodes were moved from unit IpHtml to separate units, IpHtmlTypes, IpHtmlClasses and IpHtmlNodes. This may break compilation of existing projects.<br />
* Reason: Improve maintainability of the extremely long unit IpHtml<br />
* Remedy: Add IpHtmlTypes, IpHtmlClasses and/or IpHtmlNodes to the uses clause of the project unit(s) when an "identifier not found" error referring to this package is reported by the compiler.<br />
<br />
== Other release notes ==<br />
<br />
{{Navbar Lazarus Release Notes}}<br />
<br />
[[Category:Release Notes]]<br />
[[Category:Lazarus]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=Exceptions&diff=157931Exceptions2023-12-20T22:59:36Z<p>Dbannon: Added note about issue of Windows & SEH without use SysUtils.</p>
<hr />
<div>{{Exceptions}}<br />
<br />
[[FPC|Free Pascal]] supports exceptions. Exceptions are useful for error handling and avoiding resource leaks. However, bear in mind that exceptions have a performance impact.<br />
<br />
The official documentation is here: [https://www.freepascal.org/docs-html/ref/refch17.html Reference guide chapter 17].<br />
<br />
By default exceptions are disabled. You can opt in by using the [[Mode_ObjFPC|ObjFPC]] or [[Mode_Delphi|DELPHI]] [[Compiler Mode]], or adding -Sx to the [[Compiler|compiler]] [[Command-line_interface|commandline]]. This enables the [[Try|<syntaxhighlight lang="pascal" inline>try</syntaxhighlight>]], [[Raise|<syntaxhighlight lang="pascal" inline>raise</syntaxhighlight>]], [[Except|<syntaxhighlight lang="pascal" inline>except</syntaxhighlight>]], and [[Finally|<syntaxhighlight lang="pascal" inline>finally</syntaxhighlight>]] [[Reserved word|reserved words]] for use in your own code, but it doesn't enable exception generation from the [[RTL]]. To enable the RTL to raise exceptions instead of generating [[runtime error]]s, use the SysUtils unit in your [[Program|program]].<br />
<br />
The base <syntaxhighlight lang="pascal" inline>Exception</syntaxhighlight> [[Class|class]] is found in the [https://www.freepascal.org/docs-html/rtl/sysutils/exception.html SysUtils] unit. All exceptions should preferably use this class or its descendants.<br />
<br />
SysUtils automatically sets up a basic exception catcher, so any otherwise uncaught exception is displayed in human-readable form, as the program terminates. To generate readable callstacks from caught exceptions in your own code, without necessarily terminating the program, you can use SysUtils functions <syntaxhighlight lang="pascal" inline>ExceptAddr</syntaxhighlight>, <syntaxhighlight lang="pascal" inline>ExceptFrames</syntaxhighlight>, <syntaxhighlight lang="pascal" inline>ExceptFrameCount</syntaxhighlight>, and <syntaxhighlight lang="pascal" inline>BackTraceStrFunc</syntaxhighlight>.<br />
<br />
<br />
While it is possible to raise an exception with any class descending from TObject, it is recommended to use Exception as the basis of exception class objects: the Exception class introduces properties to associate a message and a help context with the exception being raised. What is more, the SysUtils unit sets the necessary hooks to catch and display unhandled exceptions: in such cases, the message displayed to the end user, will be the message stored in the exception class.<br />
<br />
On Windows with SEH using try … except-blocks without using the SysUtils unit is currently (from 3.2.0 on) not fully supported anymore, because the SEH implementation is missing some functions to allow the usage without that unit.<br />
<br />
== Examples ==<br />
<br />
<br />
Note that Pascal uses different [[Keyword|keywords]] than some other languages: <syntaxhighlight lang="pascal" inline>raise</syntaxhighlight> instead of <syntaxhighlight lang="pascal" inline>throw</syntaxhighlight>, and <syntaxhighlight lang="pascal" inline>except</syntaxhighlight> instead of <syntaxhighlight lang="pascal" inline>catch</syntaxhighlight>. Also, as a compiler design choice, each <syntaxhighlight lang="pascal" inline>try</syntaxhighlight>-block can pair with either an <syntaxhighlight lang="pascal" inline>except</syntaxhighlight> or <syntaxhighlight lang="pascal" inline>finally</syntaxhighlight> block, but not both at the same time. You'll need to nest <syntaxhighlight lang="pascal" inline>try</syntaxhighlight>-blocks if you need <syntaxhighlight lang="pascal" inline>except</syntaxhighlight> and <syntaxhighlight lang="pascal" inline>finally</syntaxhighlight> protecting the same code.<br />
<br />
=== Error handling ===<br />
<br />
<syntaxhighlight lang="pascal"><br />
uses sysutils;<br />
<br />
begin<br />
try<br />
// Do something that might go wrong.<br />
except<br />
begin<br />
// Try to recover or show the user an error message.<br />
end;<br />
end;<br />
end.<br />
</syntaxhighlight><br />
<br />
=== Cleaning up resources ===<br />
<br />
<syntaxhighlight lang="pascal"><br />
try<br />
// Do something that might go wrong.<br />
finally<br />
// Clean-up code that is always called even if an exception was raised.<br />
end;<br />
</syntaxhighlight><br />
<br />
=== Exception leaking ===<br />
<br />
Finally-blocks don't destroy exception objects. Any exception that reaches the program's "end." will trigger a memory leak warning. An extra except block can be used to consume such exceptions. This is [https://bugs.freepascal.org/view.php?id=33650 Delphi-compatible].<br />
<br />
<syntaxhighlight lang="pascal"><br />
begin<br />
try<br />
// Your main program, where an exception object is created.<br />
finally<br />
try<br />
// Clean-up code.<br />
except<br />
end;<br />
end;<br />
end.<br />
</syntaxhighlight><br />
<br />
=== Signalling problems ===<br />
<br />
<syntaxhighlight lang="pascal"><br />
raise Exception.Create('Helpful description of what went wrong');<br />
</syntaxhighlight><br />
<br />
=== Using specialised exception types to signal different problems ===<br />
<br />
<syntaxhighlight lang="pascal"><br />
type EMyLittleException = Class(Exception);<br />
<br />
begin<br />
try<br />
raise EMyLittleException.Create('Foo');<br />
except<br />
on E : EMyLittleException do writeln(E.Message);<br />
on E : Exception do writeln('This is not my exception!');<br />
else writeln('This is not an Exception-descendant at all!');<br />
end;<br />
end;<br />
</syntaxhighlight><br />
<br />
=== Re-raising exceptions ===<br />
<br />
<syntaxhighlight lang="pascal"><br />
try<br />
// Do something that might go wrong.<br />
except<br />
// Try to recover or show the user an error message.<br />
if recoveredSuccessfully = FALSE then<br />
raise;<br />
end;<br />
</syntaxhighlight><br />
<br />
<br />
== Exception classes ==<br />
<br />
<br />
SysUtils defines and raises many specific [https://www.freepascal.org/docs-html/rtl/sysutils/index-4.html exception classes].<br />
<br />
With SysUtils included in your program, and exceptions enabled, various [https://www.freepascal.org/docs-html/user/userap4.html runtime errors] are changed into exceptions. Processor-level interrupts like SIGSEGV or SIGFPU, which normally translate to run-time errors, are also changed to exceptions. For example, run-time error 200 (division by zero) becomes EDivByZero or EZeroDivide, while run-time error 216 (a general protection fault) becomes EAccessViolation.<br />
<br />
<br />
== Best practice ==<br />
<br />
<br />
* Raise exceptions to signal that an operation could not be completed, where it normally should have succeeded.<br />
* Do not use exceptions as part of expected control flow. Instead, add checks for common error conditions and return error values the old-fashioned way. For example, input parsing problems or file existence fails are usually not truly exceptional.<br />
* But do raise an exception if it's critical that the error is noticed; programmers may forget to check for returned error values.<br />
* Naming convention: Prefix exception class names with a capital E.<br />
* Re-raise exceptions in <syntaxhighlight lang="pascal" inline>except</syntaxhighlight>-blocks using <syntaxhighlight lang="pascal" inline>raise;</syntaxhighlight> if you were unable to recover from the problem; this preserves the original exception's callstack.<br />
* Be careful about using the catch-all base <syntaxhighlight lang="pascal" inline>Exception</syntaxhighlight> class, since the underlying code or OS/filesystem might produce an unanticipated exception that slips through your exception handler, potentially corrupting the program state.<br />
* When writing a unit or library, if exceptions are used at all, they should be documented clearly up front. This way the unit user knows what to expect.<br />
* Keep exception handling away from code that needs to run as fast as possible.<br />
<br />
<br />
== Performance ==<br />
<br />
<br />
Compiler optimisation levels don't make much difference. All exception blocks use a small amount of wrapper code that can't be optimised away. There are different ways for a compiler to produce exception code, with varying performance implications. As of FPC 3.0.4, the default exception mechanism uses the standard setjmp/longjmp style. FPC also supports OS-provided Structured Exception Handling; this is the default on Win64, and can be enabled on Win32 (recompile the compiler with <syntaxhighlight lang="pascal" inline>$define TEST_WIN32_SEH</syntaxhighlight>). Other mechanisms may be added to FPC in the future.<br />
<br />
To get a better feel for what's going on, try writing a small test program and compiling it with the <syntaxhighlight lang="pascal" inline>-a</syntaxhighlight> switch. This leaves behind a human-readable assembly file.<br />
<br />
Notes on the setjmp/longjmp method:<br />
* <syntaxhighlight lang="pascal" inline>Try</syntaxhighlight>-statements insert some extra address calculations, a stack push and pop, some direct jumps, and a conditional jump. This is the setup cost of an exception frame, even if no exception is raised.<br />
* <syntaxhighlight lang="pascal" inline>Except</syntaxhighlight>-statements insert the above, plus a push and pop, more direct jumps, and another conditional jump.<br />
* <syntaxhighlight lang="pascal" inline>Finally</syntaxhighlight>-statements add a pop and a conditional jump.<br />
* <syntaxhighlight lang="pascal" inline>Raise</syntaxhighlight>-statements create a string and pass control to FPC's exception raiser. The string creation itself can spawn an implicit exception frame for the function/method, due to dynamic string allocations, even if the <syntaxhighlight lang="pascal" inline>raise</syntaxhighlight> is not executed. This is why in e.g. container types one often sees the <syntaxhighlight lang="pascal" inline>raise</syntaxhighlight> moved to a separate local (private) procedure. This localises the exception frame to that procedure, so the performance hit is only taken if the procedure is called.<br />
<br />
Furthermore, generating a human-readable callstack from a raised exception involves time-consuming stack traversal and string manipulation.<br />
<br />
With all that said, outside of heavy processing code, the convenience of exceptions usually outweighs their performance impact. Don't feel bad about using them if it makes your code better.<br />
<br />
== Further reading ==<br />
<br />
[[Logging exceptions]]<br />
<br />
[[Avoiding implicit try finally section]]<br />
<br />
[http://codenewsfast.com/cnf/article/0/permalink.art-ng53q201750 Exceptions vs Error codes] - a situation where exceptions might be a danger in some code (see middle of this page)</div>Dbannonhttps://wiki.freepascal.org/index.php?title=How_to_use_a_TrayIcon&diff=157854How to use a TrayIcon2023-12-09T03:07:23Z<p>Dbannon: How to use UnityWSCtrls.GlobalUseAppInd</p>
<hr />
<div>{{TrayIcon}}<br />
<br />
=== About ===<br />
'''[[TTrayIcon]]''' is a multiplatform System Tray component. You can find TrayIcon on the [[Additional tab]] of the [[Component Palette]] (0.9.23+).<br />
<br />
It works reliably on Windows, MacOS and Linux (and similar) with all desktops except the Gnome Desktop. It can be made work with Gnome but perhaps that's despite the Gnome Developers efforts. <br />
<br />
In use, drag the TrayIcon onto your form, assign an icon to it, assign a popup menu (with at least one menu item) to it and call its Show method <br />
<br />
To start quickly, please read [[TrayIcon#Example_1_-_Using_TIcon|the demonstration program]].<br />
<br />
=== Linux may not display the Icon ===<br />
<br />
Some Linux systems will have trouble displaying the GTK TrayIcon, in general this relates to a move away from the System Tray model that Lazarus TrayIcon is based on. This problem is a particular issue with Gnome but some other Desktops may also suffer. There are now two ways the GTK2 SysTray component tries to show its icon, the traditional System Tray and an AppIndicator library.<br />
<br />
Lazarus 2.2 (or fixes, main), now looks for an AppIndicator first and uses that if it it finds one. Only if that fails will it try for a traditional System Tray. This has several consequences -<br />
<br />
* The AppIndicator is intentionally a much simpler user interface. One click and it shows up a Popup Menu. '''No double click, no drag, no right click, no hint.''' If your app depends on one of those secondary responses, and you don't have a Gnome Desktop, you may try to force Lazarus to use the traditional SysTray, see below.<br />
<br />
* An AppIndicator library must be installed, until recently, that was libappindicator3 but now many distros are moving to libayatana. A number of prominent distributions appear to be pre installing a AppIndicator but you cannot assume.<br />
<br />
* On Gnome desktops, an additional item is needed, gnome-shell-extension-appindicator must be installed and '''enabled'''. See below.<br />
<br />
* Developers of new (Linux) applications are advised to design their program around TrayIcon using only a popUpMenu !<br />
<br />
Mageia Enlightenment also requires an AppIndicator library and enabling System Tray in Settings->Modules and then adding that System Tray to a Shelf (all terms familiar to Enlightenment users). Unfortunately, even that gets 'an' icon, not the one you carefully select for your application.<br />
<br />
==== Force traditional SysTray GTK2 ONLY====<br />
This is a purely GTK2 behavior, not for GTK3, QT5 nor any non Linux widget set. <br />
<br />
As some systems do still have a functional traditional SysTray and it does offer a richer interface, you may wish to still use it. As of Lazarus 2.0.6, GTK2 LCL apps will look at an environment variable, LAZUSEAPPIND before deciding to use the AppIndicator, if its set to NO then the traditional SysTray will be used but no error is reported (or even detectable) if its not available. If set to INFO (or anything else than NO) it will report, to the console, what it is trying to do. (The YES option became irrelevant with Lazarus 2.2.)<br />
<br />
<syntaxhighlight lang="bash">LAZUSEAPPIND=NO myapp <enter></syntaxhighlight><br />
<br />
====UnityWSCtrls.GlobalUseAppInd GTK2 ONLY====<br />
Lazarus version 3.0 and later have an additional way to predetermine which of the two TrayIcon models will be used. This has been made necessary because of the growing inconsistency of TrayIcon models in Linux Distributions. For example, at present, apparently, no 32bit Linux will work with AppIndicator model and any Gnome based distro will only work with the AppIndicator. You can set UnityWSCtrls.GlobalUseAppInd depending on some checks about the system or config file setting in the startup code of your app.<br />
<br />
To use UnityWSCtrls.GlobalUseAppInd you must include 'UnityCtrls' in the uses section of a unit, it can be set to one of the UnityWSCtrls.TUseAppIndInstruction values, (UseAppIndYes, UseAppIndNo, UseAppIndAuto). It MUST be set before your TrayIcon is created, so, put your code in the project's LPR file ahead of Application.CreateForm(TMainForm, MainForm) line, or, if you prefer, manually create your TrayIcon later in the startup process.<br />
<br />
For example, this might appear in your LPR file -<br />
<syntaxhighlight lang="pascal"><br />
uses<br />
.....<br />
{$ifdef LCLGTK2}<br />
, unitywsctrls<br />
{$endif};<br />
<br />
{$R *.res}<br />
<br />
<br />
// ----------------------------------------------------------------------------<br />
// This about reading the command line and env for instructions re TrayIcon<br />
// It applies to ONLY gtk2 and Lazarus >= 3.0.0 !<br />
// ----------------------------------------------------------------------------<br />
{$ifdef LCLGTK2}<br />
function GetUseAppInd() : UnityWSCtrls.TUseAppIndInstruction;<br />
var<br />
EnvVar : string;<br />
begin<br />
Result := UnityWSCtrls.GlobalUseAppInd;<br />
if Application.HasOption('useappind') then begin // Command line<br />
if Application.GetOptionValue('useappind') = 'yes' then // if not set, leave it alone.<br />
Result := UseAppIndYes<br />
else if Application.GetOptionValue('useappind') = 'no' then<br />
Result := UseAppIndNo; // Anything other than yes or no is ignored<br />
end else begin<br />
EnvVar := Application.EnvironmentVariable['LAZUSEAPPIND']; // EnvironmentVariable<br />
if EnvVar = 'YES' then<br />
Result := UseAppIndYes<br />
else if EnvVar = 'NO' then<br />
Result := UseAppIndNo;<br />
end;<br />
end;<br />
{$endif}<br />
<br />
begin<br />
Application.Scaled := True;<br />
Application.Title := 'tomboy-ng';<br />
RequireDerivedFormResource:=True;<br />
Application.Initialize;<br />
{$ifdef LCLGTK2 }<br />
{$ifdef CPUi386} // Note: unless Ayatana fix their problem, no option for Gnome users<br />
// https://github.com/AyatanaIndicators/libayatana-appindicator/issues/76<br />
UnityWSCtrls.GlobalUseAppInd := UnityWSCtrls.UseAppIndNo; // 32bit must be a 'no'.<br />
{$endif}<br />
UnityWSCtrls.GlobalUseAppInd := GetUseAppInd(); // Set before creating TrayIcon<br />
{$endif}<br />
Application.CreateForm(TMainForm, MainForm); <br />
....<br />
</syntaxhighlight><br />
<br />
You most certainly do not have to set UnityWSCtrls.GlobalUseAppInd, in many case, its default settings will work. But it may not work for everyone, it does give you, the programmer, some control over the situation.<br />
<br />
{{Note| At present, late 2023, every 32bit Linux I tested would generate an Access Violation when calling a GTK2 TrayIcon.Show setup to use the AppIndicator. So, better wrap it with a Try .. Except on E: EAccessViolation do... }}<br />
<br />
==== AppIndicator Libraries ====<br />
There are currently two possible AppIndicator libraries, Canonical's LibAppIndicator3-1 or libayatana-appindicator3-1, some systems have both, Bullseye has only the Ayatana one. Lazarus trunk supports Ayatana after 22 May 2021, r65122 and in 2.2.0. See External Links, below about the shift to Ayatana.<br />
<br />
One or the other library can generally be installed from your distribution's normal package library. However, it is packaged with a number of different names. Sigh ...<br />
* libappindicator3 - Slackware, NetBSD<br />
* libappindicator3-1 - openSUSE and earlier Debian [-based]<br />
* libappindicator-gtk3 - Arch, ALT, CentOS and Fedora, RedHat<br />
* libappindicator3_1 - all Mandrake/Mandriva derivatives -- Mageia, OpenMandriva, PCLinuxOS, Rosa<br />
* libayatana-appindicator3-1 - Debian Bullseye and most other Linuxs that offer Ayatana.<br />
<br />
==== gnome-shell-extension-appindicator ====<br />
This plugin is necessary for all current Gnome based systems. It is pre installed on some Gnome Desktops and only works with the an AppIndicator (not traditional SysTray). It is KNSI inspired so no fancy right clicks allowed, just assign it a menu.<br />
<br />
On Fedora, you can do this (note you don't need gnome-tweaks on Fedora 34) -<br />
<syntaxhighlight lang="bash">sudo dnf install libappindicator-gtk3 gnome-shell-extension-appindicator gnome-tweaks [enter]</syntaxhighlight><br />
Once installed, you need to restart the Desktop, just logout and back in. Then you must 'enable' the new plugin. On Fedora 33 thats easy, use the gnome-tweaks command, Extensions, enable "Kstatusnotifieritem/appindicator support". On Fedora 34, they have made it a bit harder by removing the Extensions section from Gnome Tweaks (too many happy users I expect). So, with Fedora 34, use the gnome-extensions command, it does not have a gui so issue the following command -<br />
<syntaxhighlight lang="bash"> gnome-extensions enable appindicatorsupport@rgcjonas.gmail.com [enter]</syntaxhighlight><br />
Replace 'enable' with 'info' to get some diagnostic information.<br />
<br />
Note, three steps, install, restart desktop, enable plugin ! <br />
<br />
'''Debian Bullseye''' appears to arrive with the Ayatana library pre installed but the Gnome version still requires gnome-shell-extension-appindicator, install as above.<br />
<br />
Your application can enable the extension (after a desktop restart), if you are feeling brave see https://github.com/tomboy-notes/tomboy-ng/blob/master/source/mainunit.pas<br />
<br />
'''Note :''' ''Adding the plugin as a dependency of your application might seem attractive but do not do so ! On desktops other than Gnome, it will pull in, as dependencies, the full Gnome desktop. Your end users will not thank you.''<br />
<br />
==== Checking before invoking TrayIcon ====<br />
Because your app may show the user only its TrayIcon and if that's not visible the user cannot interact, you may like to test to see if its going to work. Sadly a test previously recommend here has now proved to be unreliable (in Ubuntu 21.10) and its been removed from Lazarus.<br />
<br />
https://github.com/tomboy-notes/tomboy-ng/blob/master/source/mainunit.pas function TMainForm.CheckGnomeExtras() has code that asks the Gnome plugin manager if if the plugin is installed and enabled. And it enables it if necessary. And we really should not have to do things like that !<br />
<br />
==== On GTK3 ====<br />
Trunk from October 2019 has a working GTK3 TrayIcon based, again, on the AppIndicator. The advice above relating to installing LibAppindicator3 applies to GTK3 as well. As the only way you can get a TrayIcon under GTK3 is the an AppIndicator, you can only use it to display a popup menu, don't expect to get a working OnClick event.<br />
<br />
====QT5 on Linux====<br />
Some linux systems that won't display the GTK Tray Icon will display the Qt5 version, but only if they use XOrg. Any system using wayland apparently has other has issues with Qt5. Most Gnome desktops and some others use wayland by default but users can choose to use XOrg instead at logon time.<br />
<br />
==== Showing the Icon before establishing its Menu ====<br />
It has long been known that the System Tray Icon, on Linux at least, will not show if its .show function is called before it's popup menu has been assigned. No error shown, just no Icon to be seen.<br />
<br />
However, in 2023 with the release of Ubuntu using Gnome Desktop 44.0, a new aspect appeared. If the popup menu assigned has, itself, no menu items when .show is called, the menu is never shown even after menu items are added. In the case of gtk2, we do get an error message (complete with spelling mistake) but we see no menu. With Qt5, no error message, we get a menu but in the wrong part of the screen, and, after a being displayed a few times, we loose even that. We do not see this issue on any other OS or even any other Linux Desktop. How very Gnome-ish !<br />
<br />
So, in summary, if you are managing menu items dynamically, make sure you have at least one menu item added to the popup menu and that popup menu assigned to the TrayIcon BEFORE calling TrayIcon.show.<br />
<br />
note: chapter about 32bit carbon removed as irreverent.<br />
<br />
=== Documentation ===<br />
Below is a list of all methods, properties and events of the component. They have the same names and work the same way on the visual component and on the non-visual object.<br />
<br />
A function works on all target platforms unless written otherwise.<br />
<br />
==== Methods ====<br />
<span style="color: teal">'''Show'''</span><br />
<br />
'''procedure''' Show;<br />
<br />
Shows the icon on the system tray.<br />
<br />
<span style="color: teal">'''Hide'''</span><br />
<br />
'''procedure''' Hide;<br />
<br />
Removes the icon from the system tray.<br />
<br />
<span style="color: teal">'''GetPosition'''</span><br />
<br />
'''function''' GetPosition: TPoint;<br />
<br />
Returns the position of the tray icon on the display. This function is utilized to show message boxes near the icon. Currently it´s only a stub, no implementations are available and TPoint(0, 0) is returned.<br />
<br />
==== Properties ====<br />
<br />
<span style="color: teal">'''Hint'''</span><br />
<br />
'''property''' Hint: string;<br />
<br />
A Hint will be shown the string isn't empty<br />
<br />
<span style="color: teal">'''PopUpMenu'''</span><br />
<br />
'''property''' PopUpMenu: TPopUpMenu;<br />
<br />
A PopUp menu that appears when the user right-clicks the tray icon.<br />
<br />
==== Events ====<br />
<span style="color: teal">'''OnPaint'''</span><br />
<br />
'''property''' OnPaint: TNotifyEvent;<br />
<br />
Use this to implement custom drawing to the icon. Draw using the canvas property of the icon.<br />
<br />
{{Note| Does not work on win32.}}<br />
<br />
{{Note| Following events not available on Linux if using the LibAppIndicator3 model, its menu only.}}<br />
<br />
<span style="color: teal">'''OnClick'''</span><br />
<br />
'''property''' OnClick: TNotifyEvent;<br />
<br />
<span style="color: teal">'''OnDblClick'''</span><br />
<br />
'''property''' OnDblClick: TNotifyEvent;<br />
<br />
<span style="color: teal">'''OnMouseDown'''</span><br />
<br />
'''property''' OnMouseDown: TMouseEvent;<br />
<br />
<br />
<span style="color: teal">'''OnMouseUp'''</span><br />
<br />
'''property''' OnMouseUp: TMouseEvent;<br />
<br />
<span style="color: teal">'''OnMouseMove'''</span><br />
<br />
'''property''' OnMouseMove: TMouseMoveEvent;<br />
<br />
=== Authors ===<br />
* [[User:Sekelsenmat|Felipe Monteiro de Carvalho]]<br />
* [[User:AndrewH|Andrew Haines]]<br />
<br />
{{Note| Windows: [[User:Ozznixon|Ozz Nixon]]}}<br />
<br />
=== License ===<br />
Modified LGPL.<br />
<br />
=== Download ===<br />
Status: Stable<br />
<br />
Can be located at Lazarus 0.9.22 or inferior at the directory: lazarus/components/trayicon<br />
<br />
And on Lazarus 0.9.23 or superior it is automatically installed with LCL<br />
<br />
=== Example 1 - Using TIcon ===<br />
As of Lazarus 0.9.26 TIcon has been fully implemented and it is no longer necessary to load the icon from a resource file on Windows. The icon can be loaded in the IDE or with usual code.<br />
<br />
Go to the Additional tab of components, and add a TTrayIcon to your form. Then change its '''Name''' property to SystrayIcon<br />
<br />
Next add a button to the form. Double click the button and add this code to it:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure MyForm.Button1Click(Sender: TObject);<br />
begin<br />
SystrayIcon.Icon.LoadFromFile('/path_to_icon/icon.ico');<br />
SystrayIcon.ShowHint := True;<br />
SystrayIcon.Hint := 'my tool tip';<br />
<br />
SystrayIcon.PopUpMenu := MyPopUpMenu;<br />
<br />
SystrayIcon.Show;<br />
end;<br />
</syntaxhighlight><br />
<br />
=== Example 2 - Creating the icon with TLazIntfImage ===<br />
You can use TLazIntfImage to draw your icon quickly, as in the example code below:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.DrawIcon;<br />
var<br />
TempIntfImg: TLazIntfImage;<br />
ImgHandle, ImgMaskHandle: HBitmap;<br />
px, py: Integer;<br />
TempBitmap: TBitmap;<br />
begin<br />
try<br />
TempIntfImg := TLazIntfImage.Create(16, 16);<br />
TempBitmap := TBitmap.Create;<br />
TempBitmap.Width := 16;<br />
TempBitmap.Height := 16;<br />
TempIntfImg.LoadFromBitmap(TempBitmap.Handle, TempBitmap.MaskHandle);<br />
<br />
// Set the pixels red<br />
for py := 0 to TempIntfImg.Height - 1 do<br />
for px := 0 to TempIntfImg.Width - 1 do<br />
TempIntfImg.Colors[px, py] := colRed;<br />
<br />
// Copy it to a TBitmap<br />
TempIntfImg.CreateBitmaps(ImgHandle,ImgMaskHandle, False);<br />
TempBitmap.Handle := ImgHandle;<br />
TempBitmap.MaskHandle := ImgMaskHandle;<br />
<br />
// And copy the TBitmap to your Icon<br />
SystrayIcon.Icon.Assign(TempBitmap);<br />
SystrayIcon.Show;<br />
<br />
finally<br />
TempIntfImg.Free;<br />
TempBitmap.Free;<br />
end;<br />
end;</syntaxhighlight><br />
<br />
=== Subversion ===<br />
Located under components/trayicon/ on the latest subversion Lazarus.<br />
<br />
=== Help, Bug Reporting and Feature Request ===<br />
Please, post Bug Reports and Feature Requests on the [http://bugs.freepascal.org/main_page.php Lazarus Bugtracker].<br />
<br />
Help requests can be posted on the Lazarus mailing list or on the Lazarus [http://www.lazarus.freepascal.org/modules.php?op=modload&name=PNphpBB2&file=index Forum].<br />
<br />
=== Change Log ===<br />
# 17/01/2006 - Available as a preview on the Lazarus subversion. Still under heavy construction, however.<br />
# 24/01/2006 - Stable under win32, gnome and gtk1, but still waiting for gtk2 support. Lazarus 0.9.12 was release with this version.<br />
# 17/02/2006 - Added support for gtk2 on subversion.<br />
# July 2008 - Implements support for Qt 4<br />
# July 2008 - Implements support for Carbon through [[PasCocoa]]<br />
<br />
=== Technical Details ===<br />
A difficulty on the development of this component was the many differences on the system tray implementation on various OSes and even Window Managers on Linux. To solve this, the component tries to implement the minimal set of features common to all target platforms. Below is a list of the features implemented on each platform:<br />
<br />
'''Windows''' - Multiple system tray icons per application are supported. The image of the icon can be altered using a HICON handle. Events to the icon are sent via a special message on the user reserved space of messages (>= WM_USER) to the Window which owns the Icon. No paint events are sent to the Window.<br />
<br />
{{Note| for some odd reason the environment by default does not support WM_USER+ messages, you will need to add "-dPassWin32MessagesToLCL" (without quotations) to support the messaging code. The steps are, click Tools -> Configure "Build Lazarus"..., and add that compiler option to "Options". If you have any existing options, they are "space" delimited.}}<br />
<br />
'''Linux (Gnome, KDE, IceWM, etc)''' - Multiple system tray icons per application are supported. The image of the icon is actually a very small Window, and can be painted and receive events just like any other TForm descendant. Note that this does not apply to TrayIcons that are using the OS's AppIndicator interface. Certainly not Gnome, maybe not of desktops as time goes by.<br />
<br />
'''Linux (WindowMaker, Openbox, etc)''' - Does not support system tray icons out-of-the-box. However, There are at least two softwares that provides support for it: [http://icculus.org/openbox/2/docker/ Docker] and [http://freshmeat.net/projects/wmsystray/ WMSystray]<br />
<br />
'''macOS''' - TTrayIcon support is implemented using the menu bar extras. Unfortunately the API to use menu bar extras is only available in Cocoa and not in Carbon, so we use the stable PasCocoa bindings in the Carbon interface to support menu bar extras even in older FPC compilers and in the Cocoa interface we will use the more modern Objective Pascal syntax.<br />
<br />
[[Image:Mn_menubaritems.jpg]]<br />
<br />
To read more about menu bar extras:<br />
<br />
# https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/StatusBar/StatusBar.html<br />
# https://developer.apple.com/design/human-interface-guidelines/macos/extensions/menu-bar-extras/<br />
# https://developer.apple.com/design/human-interface-guidelines/macos/menus/dock-menus/<br />
# https://developer.apple.com/design/human-interface-guidelines/macos/system-capabilities/dock/<br />
# https://developer.apple.com/design/human-interface-guidelines/macos/menus/menu-anatomy/<br />
# http://en.wikipedia.org/wiki/Menu_extra<br />
<br />
With this in mind an approach which supports all Platforms was created:<br />
<br />
* Painting is done via a TIcon object. (Required by Windows)<br />
<br />
The following extra features are already available or will be, but they won´t work on all platforms.<br />
* OnPaint event and Canvas property to draw the icon freely. Won´t work on Windows.<br />
<br />
=== External Links ===<br />
* http://www.codeproject.com/shell/ctrayiconposition.asp - Code and theory to find the tray icon position under Windows<br />
* http://cvs.gnome.org/viewcvs/gtk%2B/gtk/gtkstatusicon.c?rev=1.23&view=markup - Gtk2 code that implements gtkstatusicon<br />
* http://pasmontray.sourceforge.net - Open source program that uses TrayIcon to display CPU and memory utilisation in the system tray.<br />
* [https://lists.debian.org/debian-devel/2018/03/msg00506.html Background to Debian's switch from libappindicator to Ayatana]<br />
* [https://github.com/AyatanaIndicators/libayatana-appindicator Ayatana App Indicator]<br />
<br />
[[Category:Tutorials]]</div>Dbannonhttps://wiki.freepascal.org/index.php?title=LCL_Defines&diff=157830LCL Defines2023-12-04T07:43:40Z<p>Dbannon: another typo !</p>
<hr />
<div>{{LCL Defines}}<br />
<br />
This page documents the defines that can be utilized to recompile the different widgetsets of LCL. It isn´t a comprehensive list, but may become one as more defines are added. It also mentions defines that are automatically set when you select various options, such as widgetset, in Lazarus. See Also [[Platform_defines]]<br />
__TOC__<br />
<br />
==Why use defines?==<br />
<br />
Defines allow to add non-standard functionality to LCL, test experimental features or to add debug output that helps testing the widgetsets.<br />
<br />
The defines are also useful as 'read only' for you to determine, for example, to include particular code on particular platforms. For example -<br />
<syntaxhighlight lang="pascal"><br />
{$ifdef WINDOWS}<br />
// do some WINDOWS specific stuff<br />
{$else}<br />
// do whatever but only on non-windows platforms<br />
{$endif}<br />
</syntaxhighlight><br />
<br />
==Recompiling LCL with a define==<br />
<br />
To recompile LCL with a special define go to the Lazarus menu Tools -> "Configure Build Lazarus". Add there in the Options field:<br />
<br />
-d<DEFINE_NAME><br />
<br />
So if you wish to add the PassWin32MessagesToLCL define, you will add <br />
<br />
-dPassWin32MessagesToLCL<br />
<br />
to this box.<br />
<br />
==General LCL defines==<br />
* '''DEBUG_SHELLCTRLS''' - Turns on the use of WriteLn to output debug information for the ShellCtrls unit.<br />
<br />
* {$WRITEABLECONST ON} - Constants declared after this are writable. Turn off with 'OFF'.<br />
<br />
====LCL Version====<br />
Sometimes its necessary to do things differently depending on the LCL (ie Lazarus) version its going to be compiled with. The LCL_FULLVERSION define can be read like this -<br />
<syntaxhighlight lang="pascal"><br />
{$if (lcl_fullversion >= 2000600)}<br />
DoSomething(); // only do this for Lazarus 2.0.6 or later<br />
{$endif}<br />
</syntaxhighlight><br />
LCL thinks of its versions in that seven digit number:<br />
<br />
* 2010000 is 2.1.0 or 2.01.00.00<br />
* 2000600 is 2.0.6 or 2.00.06.00<br />
<br />
====PLATFORM====<br />
You would not normally set one of these, they are set for you when you choose compile options in Lazarus. Useful to read to make platform specific decisions at compile time.<br />
* UNIX - eg, Linux, macOS, Ultrix<br />
* WINDOWS - all MS Windows platforms<br />
* DARWIN - macOS<br />
<br />
There are many more ....<br />
<br />
====Widget Set====<br />
You would not normally set one of these, they are set for you when you choose compile options in Lazarus. Useful to read to make widget set specific decisions at compile time.<br />
<br />
* LCLCOCOA<br />
* LCLCARBON<br />
* LCLGTK2<br />
* LCLGTK3<br />
* LCLQt (for Qt4)<br />
* LCLQt5<br />
<br />
==Win32 defines==<br />
* '''DisableWindowsUnicodeSupport''' - Turns off Unicode support on Windows. The win32 interface gives string in ansi encoding to the LCL (instead of UTF8).<br />
* '''DisableVistaDialogs''' - Turns off Windows Vista (and higher) file dialogs ([https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifiledialog IFileDialog]) and uses the older style dialogs.<br />
<br />
==WinCE defines==<br />
* '''VerboseWinCE''' - Turns on the use of writelns to output debug information. Useful in desktop windows to trace bugs which are reproducible in the desktop too.<br />
<br />
==Gtk defines==<br />
<br />
* '''LCLGTK2''' - Set when you choose the GTK2 widget set. Treat as read only.<br />
<br />
* '''LCLGTK3''' - Set when you choose the GTK3 widget set. Treat as read only.<br />
<br />
* '''DebugLCLComponents''' - This activates saving information on every creation or destruction of LCL components, device contexts, gdi objects (pen, brush, font, ...) and main widgets. It will spot mem leaks and double frees with stacktraces.<br />
<br />
* '''TraceGDICalls''' - This activates saving information on every creation or destruction of gdi objects (pen, brush, font, ...). It will spot mem leaks and double frees with stacktraces.<br />
<br />
* '''GTK2_USE_OLD_CAPTURE''' - As of lazarus 0.9.29 r27638 we use new way of capturing (TGtk2WidgetSet.SetCapture). Old way can be reached with this define.<br />
It's used only for debugging purposes (eg. diff in capturing before old & new code) if some bug is filled against gtk2 capturing.<br />
<br />
* '''USE_GTK_MAIN_OLD_ITERATION''' - As of lazarus 0.9.29 r27896 we use g_main_context* instead of g_main* which is deprecated from gtk2-2.2.<br />
This define fixes sending/receiving messages via PostMessage() from other threads than main.Also, we hook <br />
g_main_context's poll function to make g_main_context_wakeup accurate.From glib2 >= 2.24 g_main_context_wakeup is enough, but <br />
for smaller versions we need to check if new message arrived (from another thread), and interrupt main_context_iteration.<br />
For those who want to use deprecated gtk_main_* there's USE_GTK_MAIN_OLD_ITERATION.<br />
<br />
* '''GTK2OLDENUMFONTFAMILIES''' - As of lazarus 0.9.31 r35400 TGtk2WidgetSet.EnumFontFamiliesEx() does not use raw X11 fonts naming, but pango via fontconfig.<br />
Old fashioned font names can be used with this define.<br />
<br />
* '''GTK2USEDUMMYNOTEBOOKPAGE''' - As of lazarus 1.1 svn revision 39882 dummy notebook page isn't used anymore for gtk2 notebook. If some old gtk2 version<br />
needs it just use this define and pls. report issue about gtk2 version and needed dummy notebook page.<br />
<br />
==Qt defines==<br />
<br />
You can use the following defines while building LCL to configure your Qt Interface:<br />
<br />
* '''LCLQT''' - Set when you choose the Qt4 widget set. Treat as read only.<br />
<br />
* '''LCLQT5''' - Set when you choose the Qt5 widget set. Treat as read only.<br />
<br />
* '''VerboseQt''' - Writes extensive debug information about the creation and deletion of Qt objects and LCL Objects to the StdOut.<br />
<br />
* '''VerboseQtWinAPI''' - Writes extensive debug information about the Qt implementation of the functions on the LCLIntf unit to StdOut.<br />
<br />
* '''VerboseQtEvents''' - Writes extensive debug information about qt events passed via event filter to the StdOut. If defined VerboseQt then this define isn't needed.<br />
<br />
* '''VerboseQtWinAPI_MISSING_IMPLEMENTATION''' - Writes extensive debug information about the MISSING Qt implementation of the functions on the LCLIntf unit to StdOut.<br />
<br />
* '''QTOPIA''' - Build for qtopia/embedded Qt Interface. See also [[Qt4 binding#Supported_Platforms|Supported Platforms of FreePascal Qt4 binding]].Do not use QTOPIA define (comment it) if you build qt application for eg. Nokia N9 phone (arm) - it's a pure X11 app, so QTOPIA must be off.<br />
<br />
* '''QT_NATIVE_DIALOGS''' - Build with native dialogs (TOpenDialog, TSaveDialog ...). In that case there's no eg. OnFilterChanged() events since Qt uses static functions for native dialogs.<br />
<br />
* '''QTDIALOGS_USES_QT_LOOP''' - As of Lazarus svn r23536 qtlcl uses LCL loop for QDialog and QFileDialog classes (TQtDialog, TQtMessageBox, TQtFileDialog). If you want to use "old" (original Qt way) then you can use this define, and QDialogs should run in it's own loop managed by Qt. This define doesn't do anything in case we use QT_NATIVE_DIALOGS (for TQtFileDialog only).<br />
<br />
* '''QT_ENABLE_LCL_PAINT_TABS''' - as of r25354 used to allow LCL to paint over tabwidget tabs. PaintEvent from tab provides rect in tab coordinates.<br />
<br />
* '''QT_DRAWTEXT_MAP_TO_CLIPRECT''' - there's an issue (fixed already) [http://bugs.freepascal.org/view.php?id=17678] about TCanvas.TextRect() with qtlcl. If you enable QT_DRAWTEXT_MAP_TO_CLIPRECT then Qt will paint your text with respect to DT_WORDBREAK (testpaintrect_2.png), otherwise it works like other widgetsets (testpaintrect_1.png).<br />
<br />
* '''VerboseQtCustomControlResizeDeadlock''' - Writes debug information about intercepted deadlocks inside TQtCustomControl when resized.<br />
<br />
* '''DebugQtGDIObjects''' - writes debug info about TQtGdiObjects class (checks validity of HGDIObj in IsValidGDIObject())<br />
<br />
* '''QTSCROLLABLEFORMS''' - Central widgets of forms are QAbstractScrollArea class, this is enabled by default except if the QTCOCOA define is enabled. If you want less CPU/memory intrusive TCustomForms then uncomment this define. In that case we use QWidget as central widget of TCustomForm, but in that case there's no scrollbars in TCustomForm (eg TCustomForm.AutoScroll means nothing, same applies to TCustomForm. Vertical & Horizontal scrollbars.<br />
<br />
* '''QTCOCOA''' - Automatically enabled if we are on DARWIN and CPU=64 bit.If this define is enabled QTSCROLLABLEFORMS is disabled. When QTSCROLLABLEFORMS is enabled with QTCOCOA there are various crashes which aren't fixed yet.<br />
<br />
* '''QtUseNativeEventLoop''' - On non X11 targets we use the Qt native loop instead of the LCL one. Valid only for Qt5 widgetset.<br />
<br />
===Legacy===<br />
<br />
<div class="mw-collapsible mw-collapsed"><br />
Many Qt defines have become unnecessary, entries moved here for clarity.<br />
<br />
<div class="mw-collapsible-content"><br />
* <s>USE_QT_4_3 - The default Qt version utilized by the interface is 4.2 or superior. By setting this define you allow the interface to use 4.3+ features, like MDI Forms.</s> - Made default and removed as of 0.9.25<br />
* <s>QT_HIDDEN_BUTTON_WORKAROUND - This activates a work around for a LCL miss-behavior which makes it create a hidden button to overcome some Gtk problem. This should be removed after LCL is fixed. Link for the bug: http://www.freepascal.org/mantis/view.php?id=9152 </s><br />
* <s>QT_LAZARUS_IDE_WORKAROUND - Activates workaround for invisible mainmenu inside lazarus ide, also moving offsets of TGraphicControl descendants , because geometry of form doesn't take TMainMenu height and TToolbar height into account. This should be removed, as soon as we fix offsets, and accounting of TMainMenu & TToolBar height.</s><br />
* <s>VerboseQtWinAPI_DEBUG_CLIPBOARD - Writes extensive debug information about LCL clipboard operations.</s><br />
* <s>QT_NO_MENU_EFFECTS - Turns off menu effects, fading and animating, which sometimes produces problems (eg. stays visible even they are closed). It is also possible that this is an Qt bug, but have to check it first.</s><br />
* <s>USE_QT_44 - The default Qt version utilized by the interface is 4.3 or superior. By setting this define you allow the interface to use qt 4.4 features like better QPrinter,QPainter support etc.Also, if you use this define, you must download qt bindings for 4.4 (1.66 or superior).</s> as of lazarus svn r 21913 qt-4.5.X is default (no more USE_QT_XX defines).<br />
* <s>USE_QT_45 - The default Qt version utilized by the interface is 4.3 or superior. By setting this define you allow the interface to use qt 4.5 features like better QPrinter,QPainter support etc.Also, if you use this define, you must download qt bindings for 4.5 (1.69 or superior).</s> as of lazarus svn r 21913 qt-4.5.X is default (no more USE_QT_XX defines).<br />
* <s>QTSizeFix - fixes resizing overhead by reducing InvalidateClientRectCache() calls to minimum.Now only TQtPage calls it from resize event. Works ok with -dOldAutoSize and especially with new autosize algorithms.Will be set as default soon (and removed define).</s> as of lazarus svn revision 25065 this is default behaviour, define removed.<br />
</div></div><br />
<br />
==Carbon defines==<br />
List of Carbon defines used to show debug info when tracing:<br />
<br />
* DebugEventLoop<br />
* VerboseObject - Carbon interface application<br />
* VerboseTimer<br />
* VerboseWinAPI<br />
* VerboseLCLIntf<br />
* VerboseMouse<br />
* VerboseCursor<br />
* VerboseKeyboard<br />
* VerbosePaint<br />
* VerboseCanvas<br />
* VerboseMenu<br />
* VerboseScroll<br />
* VerboseWidget - TCarbonWidget class<br />
* VerboseAppEvent - Carbon application event handlers<br />
* VerboseControlEvent - Carbon control event handlers<br />
* VerboseWindowEvent - Carbon window event handlers<br />
* VerboseCommonEvent - Carbon common event handlers<br />
* VerboseWSClass - TCarbonWS classes<br />
* VerboseCarbonTrayIcon - TCarbonWSTrayIcon verbose mode<br />
<br />
Other defines:<br />
<br />
* CarbonUseCocoa - Activates using Cocoa controls. Removed in Lazarus 0.9.29<br />
* CarbonDontUseCocoa - Deactivates using Cocoa controls. Added in Lazarus 0.9.29<br />
* CarbonUsePrivateAPIs - Activates using _NSGetCarbonMenu in TTrayIcon. Not very useful at the moment.<br />
[[Category:LCL]]<br />
<br />
==FPC Optimization Defines==<br />
There are also a number of defines possible that apply to the FP Compiler - see [[Optimization#Optimization_in_Code|Optimization in Code]]<br />
<br />
{{Other Interfaces}}<br />
<br />
==See Also==<br />
<br />
[https://www.freepascal.org/docs-html/prog/progap7.html Free Pascal Compiler Documentation - Compiler defines during compilation] such as CPU, Platform, compiler ability etc.</div>Dbannonhttps://wiki.freepascal.org/index.php?title=LCL_Defines&diff=157829LCL Defines2023-12-04T07:41:33Z<p>Dbannon: typo</p>
<hr />
<div>{{LCL Defines}}<br />
<br />
This page documents the defines that can be utilized to recompile the different widgetsets of LCL. It isn´t a comprehensive list, but may become one as more defines are added. It also mentions defines that are automatically set when you select various options, such as widgetset, in Lazarus. See Also [[Platform_Defines]]<br />
__TOC__<br />
<br />
==Why use defines?==<br />
<br />
Defines allow to add non-standard functionality to LCL, test experimental features or to add debug output that helps testing the widgetsets.<br />
<br />
The defines are also useful as 'read only' for you to determine, for example, to include particular code on particular platforms. For example -<br />
<syntaxhighlight lang="pascal"><br />
{$ifdef WINDOWS}<br />
// do some WINDOWS specific stuff<br />
{$else}<br />
// do whatever but only on non-windows platforms<br />
{$endif}<br />
</syntaxhighlight><br />
<br />
==Recompiling LCL with a define==<br />
<br />
To recompile LCL with a special define go to the Lazarus menu Tools -> "Configure Build Lazarus". Add there in the Options field:<br />
<br />
-d<DEFINE_NAME><br />
<br />
So if you wish to add the PassWin32MessagesToLCL define, you will add <br />
<br />
-dPassWin32MessagesToLCL<br />
<br />
to this box.<br />
<br />
==General LCL defines==<br />
* '''DEBUG_SHELLCTRLS''' - Turns on the use of WriteLn to output debug information for the ShellCtrls unit.<br />
<br />
* {$WRITEABLECONST ON} - Constants declared after this are writable. Turn off with 'OFF'.<br />
<br />
====LCL Version====<br />
Sometimes its necessary to do things differently depending on the LCL (ie Lazarus) version its going to be compiled with. The LCL_FULLVERSION define can be read like this -<br />
<syntaxhighlight lang="pascal"><br />
{$if (lcl_fullversion >= 2000600)}<br />
DoSomething(); // only do this for Lazarus 2.0.6 or later<br />
{$endif}<br />
</syntaxhighlight><br />
LCL thinks of its versions in that seven digit number:<br />
<br />
* 2010000 is 2.1.0 or 2.01.00.00<br />
* 2000600 is 2.0.6 or 2.00.06.00<br />
<br />
====PLATFORM====<br />
You would not normally set one of these, they are set for you when you choose compile options in Lazarus. Useful to read to make platform specific decisions at compile time.<br />
* UNIX - eg, Linux, macOS, Ultrix<br />
* WINDOWS - all MS Windows platforms<br />
* DARWIN - macOS<br />
<br />
There are many more ....<br />
<br />
====Widget Set====<br />
You would not normally set one of these, they are set for you when you choose compile options in Lazarus. Useful to read to make widget set specific decisions at compile time.<br />
<br />
* LCLCOCOA<br />
* LCLCARBON<br />
* LCLGTK2<br />
* LCLGTK3<br />
* LCLQt (for Qt4)<br />
* LCLQt5<br />
<br />
==Win32 defines==<br />
* '''DisableWindowsUnicodeSupport''' - Turns off Unicode support on Windows. The win32 interface gives string in ansi encoding to the LCL (instead of UTF8).<br />
* '''DisableVistaDialogs''' - Turns off Windows Vista (and higher) file dialogs ([https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifiledialog IFileDialog]) and uses the older style dialogs.<br />
<br />
==WinCE defines==<br />
* '''VerboseWinCE''' - Turns on the use of writelns to output debug information. Useful in desktop windows to trace bugs which are reproducible in the desktop too.<br />
<br />
==Gtk defines==<br />
<br />
* '''LCLGTK2''' - Set when you choose the GTK2 widget set. Treat as read only.<br />
<br />
* '''LCLGTK3''' - Set when you choose the GTK3 widget set. Treat as read only.<br />
<br />
* '''DebugLCLComponents''' - This activates saving information on every creation or destruction of LCL components, device contexts, gdi objects (pen, brush, font, ...) and main widgets. It will spot mem leaks and double frees with stacktraces.<br />
<br />
* '''TraceGDICalls''' - This activates saving information on every creation or destruction of gdi objects (pen, brush, font, ...). It will spot mem leaks and double frees with stacktraces.<br />
<br />
* '''GTK2_USE_OLD_CAPTURE''' - As of lazarus 0.9.29 r27638 we use new way of capturing (TGtk2WidgetSet.SetCapture). Old way can be reached with this define.<br />
It's used only for debugging purposes (eg. diff in capturing before old & new code) if some bug is filled against gtk2 capturing.<br />
<br />
* '''USE_GTK_MAIN_OLD_ITERATION''' - As of lazarus 0.9.29 r27896 we use g_main_context* instead of g_main* which is deprecated from gtk2-2.2.<br />
This define fixes sending/receiving messages via PostMessage() from other threads than main.Also, we hook <br />
g_main_context's poll function to make g_main_context_wakeup accurate.From glib2 >= 2.24 g_main_context_wakeup is enough, but <br />
for smaller versions we need to check if new message arrived (from another thread), and interrupt main_context_iteration.<br />
For those who want to use deprecated gtk_main_* there's USE_GTK_MAIN_OLD_ITERATION.<br />
<br />
* '''GTK2OLDENUMFONTFAMILIES''' - As of lazarus 0.9.31 r35400 TGtk2WidgetSet.EnumFontFamiliesEx() does not use raw X11 fonts naming, but pango via fontconfig.<br />
Old fashioned font names can be used with this define.<br />
<br />
* '''GTK2USEDUMMYNOTEBOOKPAGE''' - As of lazarus 1.1 svn revision 39882 dummy notebook page isn't used anymore for gtk2 notebook. If some old gtk2 version<br />
needs it just use this define and pls. report issue about gtk2 version and needed dummy notebook page.<br />
<br />
==Qt defines==<br />
<br />
You can use the following defines while building LCL to configure your Qt Interface:<br />
<br />
* '''LCLQT''' - Set when you choose the Qt4 widget set. Treat as read only.<br />
<br />
* '''LCLQT5''' - Set when you choose the Qt5 widget set. Treat as read only.<br />
<br />
* '''VerboseQt''' - Writes extensive debug information about the creation and deletion of Qt objects and LCL Objects to the StdOut.<br />
<br />
* '''VerboseQtWinAPI''' - Writes extensive debug information about the Qt implementation of the functions on the LCLIntf unit to StdOut.<br />
<br />
* '''VerboseQtEvents''' - Writes extensive debug information about qt events passed via event filter to the StdOut. If defined VerboseQt then this define isn't needed.<br />
<br />
* '''VerboseQtWinAPI_MISSING_IMPLEMENTATION''' - Writes extensive debug information about the MISSING Qt implementation of the functions on the LCLIntf unit to StdOut.<br />
<br />
* '''QTOPIA''' - Build for qtopia/embedded Qt Interface. See also [[Qt4 binding#Supported_Platforms|Supported Platforms of FreePascal Qt4 binding]].Do not use QTOPIA define (comment it) if you build qt application for eg. Nokia N9 phone (arm) - it's a pure X11 app, so QTOPIA must be off.<br />
<br />
* '''QT_NATIVE_DIALOGS''' - Build with native dialogs (TOpenDialog, TSaveDialog ...). In that case there's no eg. OnFilterChanged() events since Qt uses static functions for native dialogs.<br />
<br />
* '''QTDIALOGS_USES_QT_LOOP''' - As of Lazarus svn r23536 qtlcl uses LCL loop for QDialog and QFileDialog classes (TQtDialog, TQtMessageBox, TQtFileDialog). If you want to use "old" (original Qt way) then you can use this define, and QDialogs should run in it's own loop managed by Qt. This define doesn't do anything in case we use QT_NATIVE_DIALOGS (for TQtFileDialog only).<br />
<br />
* '''QT_ENABLE_LCL_PAINT_TABS''' - as of r25354 used to allow LCL to paint over tabwidget tabs. PaintEvent from tab provides rect in tab coordinates.<br />
<br />
* '''QT_DRAWTEXT_MAP_TO_CLIPRECT''' - there's an issue (fixed already) [http://bugs.freepascal.org/view.php?id=17678] about TCanvas.TextRect() with qtlcl. If you enable QT_DRAWTEXT_MAP_TO_CLIPRECT then Qt will paint your text with respect to DT_WORDBREAK (testpaintrect_2.png), otherwise it works like other widgetsets (testpaintrect_1.png).<br />
<br />
* '''VerboseQtCustomControlResizeDeadlock''' - Writes debug information about intercepted deadlocks inside TQtCustomControl when resized.<br />
<br />
* '''DebugQtGDIObjects''' - writes debug info about TQtGdiObjects class (checks validity of HGDIObj in IsValidGDIObject())<br />
<br />
* '''QTSCROLLABLEFORMS''' - Central widgets of forms are QAbstractScrollArea class, this is enabled by default except if the QTCOCOA define is enabled. If you want less CPU/memory intrusive TCustomForms then uncomment this define. In that case we use QWidget as central widget of TCustomForm, but in that case there's no scrollbars in TCustomForm (eg TCustomForm.AutoScroll means nothing, same applies to TCustomForm. Vertical & Horizontal scrollbars.<br />
<br />
* '''QTCOCOA''' - Automatically enabled if we are on DARWIN and CPU=64 bit.If this define is enabled QTSCROLLABLEFORMS is disabled. When QTSCROLLABLEFORMS is enabled with QTCOCOA there are various crashes which aren't fixed yet.<br />
<br />
* '''QtUseNativeEventLoop''' - On non X11 targets we use the Qt native loop instead of the LCL one. Valid only for Qt5 widgetset.<br />
<br />
===Legacy===<br />
<br />
<div class="mw-collapsible mw-collapsed"><br />
Many Qt defines have become unnecessary, entries moved here for clarity.<br />
<br />
<div class="mw-collapsible-content"><br />
* <s>USE_QT_4_3 - The default Qt version utilized by the interface is 4.2 or superior. By setting this define you allow the interface to use 4.3+ features, like MDI Forms.</s> - Made default and removed as of 0.9.25<br />
* <s>QT_HIDDEN_BUTTON_WORKAROUND - This activates a work around for a LCL miss-behavior which makes it create a hidden button to overcome some Gtk problem. This should be removed after LCL is fixed. Link for the bug: http://www.freepascal.org/mantis/view.php?id=9152 </s><br />
* <s>QT_LAZARUS_IDE_WORKAROUND - Activates workaround for invisible mainmenu inside lazarus ide, also moving offsets of TGraphicControl descendants , because geometry of form doesn't take TMainMenu height and TToolbar height into account. This should be removed, as soon as we fix offsets, and accounting of TMainMenu & TToolBar height.</s><br />
* <s>VerboseQtWinAPI_DEBUG_CLIPBOARD - Writes extensive debug information about LCL clipboard operations.</s><br />
* <s>QT_NO_MENU_EFFECTS - Turns off menu effects, fading and animating, which sometimes produces problems (eg. stays visible even they are closed). It is also possible that this is an Qt bug, but have to check it first.</s><br />
* <s>USE_QT_44 - The default Qt version utilized by the interface is 4.3 or superior. By setting this define you allow the interface to use qt 4.4 features like better QPrinter,QPainter support etc.Also, if you use this define, you must download qt bindings for 4.4 (1.66 or superior).</s> as of lazarus svn r 21913 qt-4.5.X is default (no more USE_QT_XX defines).<br />
* <s>USE_QT_45 - The default Qt version utilized by the interface is 4.3 or superior. By setting this define you allow the interface to use qt 4.5 features like better QPrinter,QPainter support etc.Also, if you use this define, you must download qt bindings for 4.5 (1.69 or superior).</s> as of lazarus svn r 21913 qt-4.5.X is default (no more USE_QT_XX defines).<br />
* <s>QTSizeFix - fixes resizing overhead by reducing InvalidateClientRectCache() calls to minimum.Now only TQtPage calls it from resize event. Works ok with -dOldAutoSize and especially with new autosize algorithms.Will be set as default soon (and removed define).</s> as of lazarus svn revision 25065 this is default behaviour, define removed.<br />
</div></div><br />
<br />
==Carbon defines==<br />
List of Carbon defines used to show debug info when tracing:<br />
<br />
* DebugEventLoop<br />
* VerboseObject - Carbon interface application<br />
* VerboseTimer<br />
* VerboseWinAPI<br />
* VerboseLCLIntf<br />
* VerboseMouse<br />
* VerboseCursor<br />
* VerboseKeyboard<br />
* VerbosePaint<br />
* VerboseCanvas<br />
* VerboseMenu<br />
* VerboseScroll<br />
* VerboseWidget - TCarbonWidget class<br />
* VerboseAppEvent - Carbon application event handlers<br />
* VerboseControlEvent - Carbon control event handlers<br />
* VerboseWindowEvent - Carbon window event handlers<br />
* VerboseCommonEvent - Carbon common event handlers<br />
* VerboseWSClass - TCarbonWS classes<br />
* VerboseCarbonTrayIcon - TCarbonWSTrayIcon verbose mode<br />
<br />
Other defines:<br />
<br />
* CarbonUseCocoa - Activates using Cocoa controls. Removed in Lazarus 0.9.29<br />
* CarbonDontUseCocoa - Deactivates using Cocoa controls. Added in Lazarus 0.9.29<br />
* CarbonUsePrivateAPIs - Activates using _NSGetCarbonMenu in TTrayIcon. Not very useful at the moment.<br />
[[Category:LCL]]<br />
<br />
==FPC Optimization Defines==<br />
There are also a number of defines possible that apply to the FP Compiler - see [[Optimization#Optimization_in_Code|Optimization in Code]]<br />
<br />
{{Other Interfaces}}<br />
<br />
==See Also==<br />
<br />
[https://www.freepascal.org/docs-html/prog/progap7.html Free Pascal Compiler Documentation - Compiler defines during compilation] such as CPU, Platform, compiler ability etc.</div>Dbannon