Applications


Viber Desktop on Fedora 27 64bit: Viber cannot connect to the internet (OpenSSL error)

After updating our GNU/Linux distribution from Fedora 26 to Fedora 27, and maybe updating Viber Desktop client as well, Viber stopped working. To be more precise it could not connect to the internet any more. (Note: we used the RPM package of Viber Desktop to install Viber on our Fedora system).

Solution for the OpenSSL issue – Create a symbolic link to the missing library in the libraries folder of Viber

#Make sure all necessary packages are installed
sudo dnf install openssl openssl-devel;
#Create a symbolic link from the system installed libssl shared object to the Viber installation folder
sudo ln -s /usr/lib64/libssl.so.10 /opt/viber/lib/libssl.so;

Explanation and details – Optional Reading

To find the installation folder of Viber, we used the following command:

find / -iname viber 2>/dev/null;

that led us to the installation location of Viber which was /opt/viber/Viber.

Executing the binary in a terminal gave us the following errors

$ /opt/viber/Viber
 Qt WebEngine ICU data not found at /opt/viber/resources. Trying parent directory...
 Qt WebEngine resources not found at /opt/viber/resources. Trying parent directory...
 Qt WebEngine ICU data not found at /opt/viber/resources. Trying parent directory...
 Qt WebEngine resources not found at /opt/viber/resources. Trying parent directory...
 qt.network.ssl: QSslSocket: cannot resolve CRYPTO_num_locks
 qt.network.ssl: QSslSocket: cannot resolve CRYPTO_set_id_callback
 qt.network.ssl: QSslSocket: cannot resolve CRYPTO_set_locking_callback
 qt.network.ssl: QSslSocket: cannot resolve ERR_free_strings
 qt.network.ssl: QSslSocket: cannot resolve EVP_CIPHER_CTX_cleanup
 qt.network.ssl: QSslSocket: cannot resolve EVP_CIPHER_CTX_init
 qt.network.ssl: QSslSocket: cannot resolve sk_new_null
 qt.network.ssl: QSslSocket: cannot resolve sk_push
 qt.network.ssl: QSslSocket: cannot resolve sk_free
 qt.network.ssl: QSslSocket: cannot resolve sk_num
 qt.network.ssl: QSslSocket: cannot resolve sk_pop_free
 qt.network.ssl: QSslSocket: cannot resolve sk_value
 qt.network.ssl: QSslSocket: cannot resolve SSL_library_init
 qt.network.ssl: QSslSocket: cannot resolve SSL_load_error_strings
 qt.network.ssl: QSslSocket: cannot resolve SSL_get_ex_new_index
 qt.network.ssl: QSslSocket: cannot resolve SSLv23_client_method
 qt.network.ssl: QSslSocket: cannot resolve SSLv23_server_method
 qt.network.ssl: QSslSocket: cannot resolve X509_STORE_CTX_get_chain
 qt.network.ssl: QSslSocket: cannot resolve OPENSSL_add_all_algorithms_noconf
 qt.network.ssl: QSslSocket: cannot resolve OPENSSL_add_all_algorithms_conf
 qt.network.ssl: QSslSocket: cannot resolve SSLeay
 qt.network.ssl: QSslSocket: cannot resolve SSLeay_version
 qt.network.ssl: Incompatible version of OpenSSL
 qt.network.ssl: QSslSocket: cannot call unresolved function SSLv23_client_method
 qt.network.ssl: QSslSocket: cannot call unresolved function SSL_library_init
 QQuickItem: Cannot set activeFocusOnTab to false once item is the active focus item.
 QQuickItem: Cannot set activeFocusOnTab to false once item is the active focus item.
 qt.network.ssl: QSslSocket: cannot call unresolved function SSLv23_client_method
 qt.network.ssl: QSslSocket: cannot call unresolved function SSL_library_init
 QQuickItem: Cannot set activeFocusOnTab to false once item is the active focus item.
 qt.network.ssl: QSslSocket: cannot call unresolved function SSLv23_client_method
 qt.network.ssl: QSslSocket: cannot call unresolved function SSL_library_init

From the error we could see that the application was trying to access the functions SSLv23_client_method and SSL_library_init and it could not find them.
It was obvious that the missing functions were related to the SSL protocol, so we executed the following to make sure all necessary OpenSSL packages were installed.

sudo dnf install openssl openssl-devel;

After that we tried Viber again and gave us the same error.
So we made the assumption that Viber could not ‘see’ the installation of OpenSSL.
Then we decided to use the following command to:

  1. find all shared objects (shared libraries) of OpenSSL on our system,
  2. list all of the available functions in each shared library and
  3. identify the ones that contain the SSLv23_client_method method.
find / -iname libssl.so* -print -exec sh -c 'nm -D "$1" | grep SSLv23_client_method' _ {} \; 2>/dev/null;

Executing the command yielded the following results:

$ find / -iname libssl.so* -print -exec sh -c 'nm -D "$1" | grep SSLv23_client_method' _ {} \; 2>/dev/null;
 /usr/lib/libssl.so
 /usr/lib/libssl.so.1.1
 /usr/lib/libssl.so.10
 00027880 T SSLv23_client_method
 /usr/lib/libssl.so.1.0.2m
 00027880 T SSLv23_client_method
 /usr/lib/libssl.so.1.1.0g
 /usr/lib64/libssl.so
 /usr/lib64/libssl.so.1.1
 /usr/lib64/libssl.so.10
 000000000002dd10 T SSLv23_client_method
 /usr/lib64/libssl.so.1.0.2m
 000000000002dd10 T SSLv23_client_method
 /usr/lib64/libssl.so.1.1.0g
 /opt/viber/lib/libssl.so

From the results two libraries in the lib64 folder got our interest, so we executed ls -l on both to check them out:

$ ls -l /usr/lib64/libssl.so.10 /usr/lib64/libssl.so.1.0.2m;
lrwxrwxrwx. 1 root root 16 Nov 13 13:52 /usr/lib64/libssl.so.10 -> libssl.so.1.0.2m
-rwxr-xr-x. 1 root root 448640 Nov 13 13:52 /usr/lib64/libssl.so.1.0.2m

As you can see in the results, the two files that got us interested were in end the same file so we decided to use /usr/lib64/libssl.so.10 whose name was not tight to a minor distribution version of the library and would automatically link to any minor updates in the future.

After examining the Viber installation folder in /opt/viber/, we saw that there was a lib folder there with no SSL libraries available (even though all the QT and networking libraries were there already):

$ ls /opt/viber/lib/
libicudata.so.52 libQt5Gui.so.5 libQt5Qml.so.5 libQt5WebEngine.so.5
libicui18n.so.52 libQt5Location.so.5 libQt5QuickControls2.so.5 libQt5WebEngineWidgets.so.5
libicuuc.so.52 libQt5MultimediaQuick_p.so.5 libQt5Quick.so.5 libQt5WebSockets.so.5
libpng12.so.0 libQt5Multimedia.so.5 libQt5QuickTemplates2.so.5 libQt5Widgets.so.5
libqgsttools_p.so.1 libQt5MultimediaWidgets.so.5 libQt5QuickWidgets.so.5 libQt5XcbQpa.so.5
libqrencode.so libQt5Network.so.5 libQt5Sql.so.5
libQt5Concurrent.so.5 libQt5OpenGL.so.5 libQt5Svg.so.5 libxcb-xinerama.so.0
libQt5Core.so.5 libQt5Positioning.so.5 libQt5WebChannel.so.5
libQt5DBus.so.5 libQt5PrintSupport.so.5 libQt5WebEngineCore.so.5

Examining our OS, we saw that in the rest of the locations were the QT libraries were installed there was a copy of the libssl as well.
So we decided to make a soft symbolic link of the /usr/lib64/libssl.so.10 shared library in the Viber libraries folder.
We did two tests:

  1. Creating a symbolic link named libssl.so.10 failed.
  2. Creating a symbolic link name libssl.so succeeded and solved the issue!

The command that we used was the following:

sudo ln -s /usr/lib64/libssl.so.10 /opt/viber/lib/libssl.so;

After performing this step, Viber was working again as expected!

$ sudo ln -s /usr/lib64/libssl.so.10 /opt/viber/lib/libssl.so;
 $ /opt/viber/Viber
 Qt WebEngine ICU data not found at /opt/viber/resources. Trying parent directory...
 Qt WebEngine resources not found at /opt/viber/resources. Trying parent directory...
 Qt WebEngine ICU data not found at /opt/viber/resources. Trying parent directory...
 Qt WebEngine resources not found at /opt/viber/resources. Trying parent directory...
 QQuickItem: Cannot set activeFocusOnTab to false once item is the active focus item.
 QQuickItem: Cannot set activeFocusOnTab to false once item is the active focus item.
 QQuickItem: Cannot set activeFocusOnTab to false once item is the active focus item.
 QQuickItem: Cannot set activeFocusOnTab to false once item is the active focus item.
 QQuickItem: Cannot set activeFocusOnTab to false once item is the active focus item.
 qml: type=""
 qml: type=""
 qrc:/QML/Feed/FeedView.qml:98:5: QML ListViewEx: Binding loop detected for property "bottomMargin"
 QObject: Cannot create children for a parent that is in a different thread.
 (Parent is QObject(0x7f3f5f970520), parent's thread is QThread(0x4191210), current thread is QThread(0x515f5b0)
 Invalid SOS parameters for sequential JPEG

Success!


Android Studio – No space left on device 1

Recently we tried to install some packages from the Android SDK through Android Studio 3.0.1 and we got the error that the PC ran out of space while downloading the necessary packages and could not perform the operation.

Full Error Log:

To install:
- Google APIs Intel x86 Atom System Image (system-images;android-27;google_apis;x86)
Preparing "Install Google APIs Intel x86 Atom System Image (revision: 2)".
Downloading https://dl.google.com/android/repository/sys-img/google_apis/x86-27_r02.zip
An error occurred while preparing SDK package Google APIs Intel x86 Atom System Image: Cannot download 'https://dl.google.com/android/repository/sys-img/google_apis/x86-27_r02.zip': No space left on device
, response: 200 OK.
"Install Google APIs Intel x86 Atom System Image (revision: 2)" failed.
Failed packages:
- Google APIs Intel x86 Atom System Image (system-images;android-27;google_apis;x86)

Although the disk had a lot of free space, we got this error because the /tmp partition on our GNU/Linux Fedora 27 workstation was too small (~4GB).

$ df -h;
Filesystem Size Used Avail Use% Mounted on
devtmpfs 3.9G 0 3.9G 0% /dev
tmpfs 3.9G 107M 3.8G 3% /dev/shm
tmpfs 3.9G 1.9M 3.9G 1% /run
tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup
/dev/mapper/fedora-root 50G 35G 12G 75% /
tmpfs 3.9G 2.1M 3.9G 1% /tmp
/dev/sdb1 477M 179M 269M 41% /boot
/dev/mapper/fedora-home 178G 37G 132G 22% /home
tmpfs 794M 16K 794M 1% /run/user/42
tmpfs 794M 9.2M 785M 2% /run/user/1000

/tmp was not explicitly set so it was automatically configured to have half of the size of the RAM.
We didn’t not want to change the download path for the Java environment (and hence Android Studio and Android SDK tools) as after leaving the tmpfs folder it could mean that we would have to manually maintain the new path and clean it up. So we ended up in temporarily increasing the size of /tmp partition which did the trick and the virtual device was installed successfully.

Solution – temporarily increase the size of the /tmp partition:

The command we used to increase the size of the /tmp partition on Fedora 27 was the following:

sudo mount -o remount,size=8G,noatime /tmp;

After executing we, it we immediately saw that the results were applied without the need for a restart or any other operation and we could proceed to use the PC as normal.

$ df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 3.9G 0 3.9G 0% /dev
tmpfs 3.9G 89M 3.8G 3% /dev/shm
tmpfs 3.9G 1.9M 3.9G 1% /run
tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup
/dev/mapper/fedora-root 50G 35G 12G 75% /
tmpfs 8.0G 2.0M 8.0G 1% /tmp
/dev/sdb1 477M 179M 269M 41% /boot
/dev/mapper/fedora-home 178G 37G 132G 22% /home
tmpfs 794M 16K 794M 1% /run/user/42
tmpfs 794M 6.9M 787M 1% /run/user/1000

The information for tmpfs we got it from: https://wiki.archlinux.org/index.php/Tmpfs

Full Correct Execution Log:

To install:
- Google APIs Intel x86 Atom System Image (system-images;android-27;google_apis;x86)
Preparing "Install Google APIs Intel x86 Atom System Image (revision: 2)".
Downloading https://dl.google.com/android/repository/sys-img/google_apis/x86-27_r02.zip
"Install Google APIs Intel x86 Atom System Image (revision: 2)" ready.
Installing Google APIs Intel x86 Atom System Image in /home/xeirwn/Android/Sdk/system-images/android-27/google_apis/x86
"Install Google APIs Intel x86 Atom System Image (revision: 2)" complete.
"Install Google APIs Intel x86 Atom System Image (revision: 2)" finished.


How to process tcpdump live data stream from a remote machine on a local WireShark

Recently we needed to process the results of a tcpdump command using the GUI version of WireShark on machine that did not have a window manager installed. That device was an embedded device, for which it did not make sense to even consider installing a window manager on it. So, in order to process the results of the tcpdump command we decided to use another machine that had a full working window manager installed and was able to operate the GUI version of WireShark.

For our solution to work some requirements were expected to be met by the embedded device (a.k.a. remote machine).

  1. tcpdump was installed on the remote machine
  2. ssh server was installed on the remote machine and allowed us to connect to it remotely
  3. there was a user that had remote ssh rights on the remote machine that also had the rights to execute tcpdump on the needed interfaces

Synopsis of our solution:

Just execute the following on the machine with the GUI (a.k.a. local machine)

mkfifo /tmp/board;
wireshark -k -i /tmp/board &
ssh [email protected] "tcpdump -s 0 -U -n -w - -i lo not port 22" > /tmp/board;

Explanation of our solution:

Following are the steps that we performed on the local machine to pipe the results of tcpdump on the remote machine on the wireshark on the local machine.

  1. First we created a named pipe as follows:
    mkfifo /tmp/board;
    You can name your pipe anyway you like and place it in any folder you wish. We used /tmp as our pipe is a temporary construct that we do not care to preserve across time/restarts.
  2. Then we started wireshark from a terminal so that we could pass as capture interface the named pipe we just created using the -i /tmp/board parameter. The -k parameter instructs wireshark to start the capture session immediately.
    wireshark -k -i /tmp/board &
    Since this operation was going to execute for a long time, we sent it to the background to release the terminal for further use by placing the & symbol at the end of the command.
  3. Finally, we started tcpdump over ssh on a board and redirected its output to our named pipe.
    ssh [email protected] "tcpdump -s 0 -U -n -w - -i lo not port 22" > /tmp/board;
    The parameters we used on tcpdump have the following effects:
    -s 0 instructs tcpdump to set the snapshot length of data from each packet to the default value of 262144 bytes.
    -U Since the -w option is not specified, make the printed packet output packet-buffered. Which means that it will print the description of the contents of each packet without waiting for the output buffer to get full.
    -n Does not convert host addresses to names. This can be used to avoid DNS lookups.
    -w - Write the raw packets to Standard Output rather than parsing them.
    -i lo Defines which interface to listen on. We wanted the loopback interface to listen to everything.
    not port 22 Since we used ssh to start this command, we do not want to listen to the data that we produce as well and flood the inputs.

 


Enable C++11 standard for GCC on Eclipse CDT

When using Eclipse CDT to write C++, we noticed that it did not enable by default the C++11 standard. Following the steps below, we added the -std=c++11 flag on the GCC C++ Compiler command line arguments enabling the standard for our use.

  1. From the main window of Eclipse, on the list on the left, where your projects are listed, right click on your project and then click Properties from the new menu
  2. In the new window, navigate from the list on the left and expand the C/C++ Build option to view its children and then click the Settings item
  3. In the middle of the window, you will see a new list, expand (if needed) the item GCC C++ Compiler and click on the Miscellaneous child
  4. On the right, a text box named Other Flags will appear, append -std=c++11 to the list of tokens in the box as seen in the image below
  5. Click on the Apply button for the effects to take place and then the OK button to close the properties window

Next time you compile, the -std=c++11 flag will be present on your compiler command line and the C++11 standard will be used.


Start Arduino IDE as root on Fedora / Allow root to start an X application

Solution

Execute the following as a normal user

xhost +si:localuser:root;
sudo ./arduino;

Background Story and More Information

Recently we needed to start the official Arduino IDE as root on Fedora to allow the application to take control of the serial port.
We were getting the following error whenever we tried to upload the application to the board:

processing.app.debug.RunnerException
 at cc.arduino.packages.uploaders.SerialUploader.uploadUsingPreferences(SerialUploader.java:160)
 at cc.arduino.UploaderUtils.upload(UploaderUtils.java:78)
 at processing.app.SketchController.upload(SketchController.java:713)
 at processing.app.SketchController.exportApplet(SketchController.java:686)
 at processing.app.Editor$DefaultExportHandler.run(Editor.java:2168)
 at java.lang.Thread.run(Thread.java:748)
Caused by: processing.app.SerialException: Error touching serial port '/dev/ttyACM0'.
 at processing.app.Serial.touchForCDCReset(Serial.java:107)
 at cc.arduino.packages.uploaders.SerialUploader.uploadUsingPreferences(SerialUploader.java:144)
 ... 5 more
Caused by: jssc.SerialPortException: Port name - /dev/ttyACM0; Method name - openPort(); Exception type - Permission denied.
 at jssc.SerialPort.openPort(SerialPort.java:170)
 at processing.app.Serial.touchForCDCReset(Serial.java:101)
 ... 6 more

So, we tried to start the arduino IDE using root and got another error:

[[email protected] bin]$ sudo ./arduino;
[sudo] password for george: 
No protocol specified
Picked up JAVA_TOOL_OPTIONS: 
No protocol specified
java.awt.AWTError: Can't connect to X11 window server using ':0' as the value of the DISPLAY variable.
	at sun.awt.X11GraphicsEnvironment.initDisplay(Native Method)
	at sun.awt.X11GraphicsEnvironment.access$200(X11GraphicsEnvironment.java:65)
	at sun.awt.X11GraphicsEnvironment$1.run(X11GraphicsEnvironment.java:115)
	at java.security.AccessController.doPrivileged(Native Method)
	at sun.awt.X11GraphicsEnvironment.(X11GraphicsEnvironment.java:74)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:264)
	at java.awt.GraphicsEnvironment.createGE(GraphicsEnvironment.java:103)
	at java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment(GraphicsEnvironment.java:82)
	at sun.awt.X11.XToolkit.(XToolkit.java:126)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:264)
	at java.awt.Toolkit$2.run(Toolkit.java:860)
	at java.awt.Toolkit$2.run(Toolkit.java:855)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.awt.Toolkit.getDefaultToolkit(Toolkit.java:854)
	at java.awt.SystemColor.updateSystemColors(SystemColor.java:473)
	at java.awt.SystemColor.(SystemColor.java:465)
	at processing.app.Theme.init(Theme.java:84)
	at processing.app.Base.(Base.java:219)
	at processing.app.Base.main(Base.java:144)

This error occurred because the default configuration of the X server permissions did not allow the root to connect to it.
To verify this, we used xhost (the X server access control program) to check the permissions.
Executing xhost with no command line arguments gave us a message indicating whether or not access control was currently enabled, followed by the list of those users allowed to connect.
For example in our case the output was as follows:

[[email protected] bin]$ xhost
access control enabled, only authorized clients can connect
SI:localuser:george

To add root to the list of users that was allowed to start an X application we executed the following command:

[[email protected] bin]$ xhost +si:localuser:root
localuser:root being added to access control list

Executing xhost again, we got the updated list which included the root

[[email protected] bin]$ xhost
access control enabled, only authorized clients can connect
SI:localuser:root
SI:localuser:george

After this, we were able to start arduino IDE using sudo with no problems.

[[email protected] bin]$ sudo ./arduino;

Note: This patch is not permanent, we actually execute it once at every restart of the machine.


Create a graph out of an XML file

To create a graph out of an XML file, we used the yEd graph editor.
yEd is a desktop application that can be used to generate diagrams. It is free of charge (not FOSS) and runs on all major platforms: Windows, Unix/Linux, and Mac OS X.

For the following demo we used the latest release for GNU/Linux that was available at the time (yEd-3.17.1).

Usage Example

  1. Download yEd
  2. Extract it
  3. Navigate into the generated folder (e.g. if you used the attached version then the folder will be yed-3.17.1)
  4. Start the application using a JRE. In our case the command to start it was: java -jar yed.jar
  5. Click on the Open option (or go to File > Open... )
  6. Navigate to the XML file that you want to graph and click Open
  7. In the pop-up window for the drop-down named XSL File for then select the option XML-Tree and click Ok
  8. By now you should have your graph available to view/edit/export

IEEETran Latex Template – Add Copyrights on left bottom of first page of Conference class

Using the conference class in the IEEETran Latex template, it blocks you from using the IEEEpubid command that can be used to add the copyright note on the bottom of the screen.

Attempt 1 – Enable overrides only

We added the IEEEoverridecommandlockouts command right after the documentclass directive, that allows us to use functionality in IEEETran that should be blocked depending on the document class.
Then we used the IEEEpubid command right after the documentclass directive to create and print the copyright note:

\IEEEpubid{978-1-5386-2880-5/17/\$31.00~\copyright~2017 IEEE}

Doing so, resulted in printing the copyright note in the bottom middle of the first page. The conference class is a two column style, the left column got pushed up a bit to make space, while the right did not. In the end, half of the copyright note was overlapping with the second column.

Attempt 2 – Solution – Customize IEEEpubid as well

After documentclass we added the IEEEoverridecommandlockouts command to enable functionality that is blocked for the conference class.
Then we created our own IEEEpudid command that places itself in a column and not a page as follows:

\documentclass[conference]{IEEEtran}
%Enabled blocked functionality
\IEEEoverridecommandlockouts
%Define custom IEEEpubid that will place it self in a column and not a page, suitable from conference class
\IEEEpubid{\makebox[\columnwidth]{978-1-5386-2880-5/17/\$31.00~\copyright~2017 IEEE \hfill} \hspace{\columnsep}\makebox[\columnwidth]{ }}

Finally after the maketitle command we added IEEEpubidadjcol to give more space between the copyright note and the body of first column.

\maketitle
%Add space between copyright and text
\IEEEpubidadjcol

Making an animated torus in gnuplot and gimp

The gnuplot code in this article, creates several frames of a 3D torus with a visible structural grid from different viewing angles, like the one in the image below.

torus.gnuplot (compressed) (98 downloads)

In geometry, a torus (plural tori) is a surface of revolution generated by revolving a circle in three-dimensional space about an axis coplanar with the circle. If the axis of revolution does not touch the circle, the surface has a ring shape and is called a torus of revolution.

From Wikipedia https://en.wikipedia.org/wiki/Torus

By modifying the circles and rings variables in the script you can increase and decrease the number of circles and rings that appear on your torus.
The above image was generated using 60 circles and 30 rings.

To better explain what a circle and what a ring is, please have a look at the following two examples.

The following torus has 10 circles and 30 rings.

The next one has 60 circles and 10 rings.

The last torus has 10 circles and 10 rings.

Using the script, you can also play around with the set view command and the for loop to change the number of frames that will be produced and what movement should the camera perform before printing.

torus.gnuplot (compressed) (98 downloads)

set terminal pngcairo transparent enhanced font "arial,10" fontscale 1.0 size 700, 400

unset key
unset border
unset tics

set dummy u, v

circles=60
rings=30

set parametric
set isosamples circles, rings
set hidden3d back offset 1 trianglepattern 3 undefined 1 altdiagonal bentover
set urange [ -pi : pi ] noreverse nowriteback
set vrange [ -pi : pi ] noreverse nowriteback

do for [i=1:360/circles] {
 set view 25, i, 1, 1
 set output sprintf('game-of-life-torus.%03.0f.png', i)
 splot cos(u)+.5*cos(u)*cos(v),sin(u)+.5*sin(u)*cos(v),.5*sin(v) with lines
}

torus.gnuplot (compressed) (98 downloads)

Below, you can see two examples of animation we build together using the gimp application.

Showing one frame at a time.

Accumulating all frames into one, until the loop is reset.

To create the animation we followed the next steps:

  1. Executed gnuplot torus.gnuplot to generated the frames.
  2. Then we started the gimp and from the menu File we chose the option Open as layers... (Ctrl + Alt + o).
  3. We selected all the images we wanted for our animation and pressed the Open button.
  4. The Layers - Brushes window got populated by the new frames. Please note that window you can use that to change the order of the frames (which are now the layers).
  5. Following, from the menu File we chose the option Export As... (Shift + Ctrl + E), from the pop-up window we selected the type of the file to be gif and pressed the Export button.
  6. At the final pop-up window, we enabled the As animation checkbox, then at the Frame disposal where unspecified dropdown menu we chose One frame per layer (replace) and hit the Export button which produce the first animation in this article (two images up).

This figure shows the ‘Layers – Brushes’ window that got populated by the new frames.

From the menu File we chose the option Export As… (Shift + Ctrl + E), from the pop-up window we selected the type of the file to be ‘gif’ and pressed the Export button.

At the final pop-up window, we enabled the As animation checkbox, then at the Frame disposal where unspecified dropdown menu we chose One frame per layer (replace) and hit the Export button which produce the first animation in this article (two images up).