Mittwoch, 15. Februar 2023

The Maemulator: Running a certain 2009 tech demo on modern Linux

Want to revisit 2009's N900 tech demo but you got rid of your old toys long ago or don't want to bother digging them out of your desk drawer? The Maemulator to the rescue! It uses QEMU user-space emulation and some LD_PRELOAD magic + other in-process trickery to get it working on any modern Linux machine that has an OpenGL driver. Add multi-sample anti-aliasing, anisotropic filtering, higher resolutions, keyboard input and various fixes, and you are all set for a few minutes of fun distraction.



Donnerstag, 1. Juli 2021

Loonies 8192 now for Maemo 4 (N800) and MeeGo 1.2 Harmattan (N9)

As part of a summer clean-up of the desk drawers, I pulled out the N800 and N9 and ported my game Loonies 8192 to these devices. Since those are "proper" Linux devices, one can compile things directly on-device (just install gcc from the SDK repos), and with SSH, it's easy to type on a real keyboard.

Anyway, you can install the game via the landing pages:

For the N800, make sure "maemo Extras" is enabled so it will find libsdl1.2 if it's not already installed. Head over to https://loonies.thp.io/n800/ on the device and download the deb, it will be installed by Application manager.

For the N9, make sure you have n9repomirror installed (again, so libsdl1.2 can be installed if necessary). Enable third party applications in Settings, Applications, Installations. Then head over to https://loonies.thp.io/n9/ on the device and download the deb, selecting after the download is finished will ask you to install it.

The N9 version is also available on openrepos.net.

And don't forget that the game is also available for DOS, various consoles and handheld consoles as well as on Windows. All of the builds are available on itch.io.

Donnerstag, 11. Februar 2021

reBounce - softfp-to-hardfp LD_PRELOAD hack for Bounce on N9

This depends on Bounce (the N900 .deb) and SDL 1.2 being installed. Google "bounce_1.0.0_armel.deb" for the former, and use n9repomirror for the latter.

This is available on OpenRepos: https://openrepos.net/content/thp/rebounce

Source code (copy'n'paste this into a file named "rebounce.c", then run it using your shell):

#if 0

gcc -Os -shared -fPIC -lSDL -o librebounce.so rebounce.c

LD_PRELOAD=$(pwd)/librebounce.so /opt/bounce/bin/bounce

exit 0

#endif


/**

 * reBounce -- softfp-to-hardfp LD_PRELOAD hack for Bounce on N9

 *

 * Bounce was a really nice 2009 tech demo on the N900. This

 * makes this tech demo work on an N9 by translating the calls

 * that use floating point arguments to the hardfp ABI. It also

 * fixes input using SDL1.2 to get sensor values from sensorfw.

 *

 * Known issues: Audio is muted on startup until mute is toggled.

 *

 * 2021-02-11 Thomas Perl <m@thp.io>

 **/


#define _GNU_SOURCE


#include <stdio.h>

#include <dlfcn.h>

#include <pthread.h>

#include <SDL/SDL.h>


#define SFP __attribute__((pcs("aapcs")))


typedef unsigned int GLuint;


static void *

sensor_thread(void *user_data)

{

    SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_VIDEO);


    SDL_Joystick *accelerometer = SDL_JoystickOpen(0);


    while (1) {

        SDL_JoystickUpdate();


        float x = 0.053888f * SDL_JoystickGetAxis(accelerometer, 0);

        float y = 0.053888f * SDL_JoystickGetAxis(accelerometer, 1);

        float z = 0.053888f * SDL_JoystickGetAxis(accelerometer, 2);


        FILE *out = fopen("/dev/shm/bounce.sensor.tmp", "wb");

        fprintf(out, "%f %f %f\n", -y, x, z);

        fclose(out);

        rename("/dev/shm/bounce.sensor.tmp", "/dev/shm/bounce.sensor");


        SDL_Delay(10);

    }


    return NULL;

}


FILE *

fopen(const char *filename, const char *mode)

{

    FILE *(*fopen_orig)(const char *, const char *) = dlsym(RTLD_NEXT, "fopen");

    if (strcmp(filename, "/sys/class/i2c-adapter/i2c-3/3-001d/coord") == 0) {

        static int sensor_inited = 0;

        if (!sensor_inited) {

            sensor_inited = 1;


            pthread_t thread;

            pthread_create(&thread, NULL, sensor_thread, NULL);

        }


        filename = "/dev/shm/bounce.sensor";

    }


    return fopen_orig(filename, mode);

}


#define f_f(name) float SFP name(float x) { return ((float (*)(float))dlsym(RTLD_NEXT, #name))(x); }

#define d_d(name) double SFP name(double x) { return ((double (*)(double))dlsym(RTLD_NEXT, #name))(x); }

#define f_ff(name) float SFP name(float x, float y) { return ((float (*)(float, float))dlsym(RTLD_NEXT, #name))(x, y); }

#define d_dd(name) double SFP name(double x, double y) { return ((double (*)(double, double))dlsym(RTLD_NEXT, #name))(x, y); }


f_f(sinhf) f_f(coshf) f_f(tanhf) f_f(asinf) f_f(acosf) f_f(atanf) f_f(sinf) f_f(cosf) f_f(tanf) f_f(expf) f_f(logf)

f_f(log10f) f_f(ceilf) f_f(floorf) d_d(log) d_d(sin) f_ff(atan2f) f_ff(fmodf) d_dd(atan2) d_dd(pow) d_dd(fmod)


double SFP

frexp(double value, int *exp)

{

    return ((double (*)(double, int *))dlsym(RTLD_NEXT, "frexp"))(value, exp);

}


double SFP

ldexp(double x, int n)

{

    return ((double (*)(double, int))dlsym(RTLD_NEXT, "ldexp"))(x, n);

}


double SFP

modf(double value, double *iptr)

{

    return ((double (*)(double, double *))dlsym(RTLD_NEXT, "modf"))(value, iptr);

}


void SFP

glClearColor(float r, float g, float b, float a)

{

    ((void (*)(float, float, float, float))dlsym(RTLD_NEXT, "glClearColor"))(r, g, b, a);

}


void SFP

glUniform4f(GLuint location, float v0, float v1, float v2, float v3)

{

    ((void (*)(GLuint, float, float, float, float))dlsym(RTLD_NEXT, "glUniform4f"))(location, v0, v1, v2, v3);

}


void SFP

glUniform1f(GLuint location, float v0)

{

    ((void (*)(GLuint, float))dlsym(RTLD_NEXT, "glUniform1f"))(location, v0);

}


Donnerstag, 29. Januar 2015

Running Debian sid on Sailfish OS

This assumes you have a Debian/Ubuntu host computer on which to run debootstrap. Theoretically you can run this on the device, but it's not as easy as on Harmattan (where you can just install the debootstrap package. On the host, run the first init and create a tarball:
    sudo debootstrap --arch armhf --foreign sid sid
    sudo tar czvf sid.tgz -C sid .
    du -sh sid.tgz 
    # 98M     sid.tgz
    scp sid.tgz nemo@192.168.2.15:
To unpack the chroot tarball:
    ssh nemo@192.168.2.15
    devel-su
    # password

    mkdir sid
    cd sid
    tar xvf ../sid.tgz
    chroot /home/nemo/sid/ /debootstrap/debootstrap --second-stage
    chroot /home/nemo/sid/ apt-get clean
    echo "deb http://http.debian.net/debian sid main" \
        >/home/nemo/sid/etc/apt/source.list
To enter the chroot:
    ssh nemo@192.168.2.15
    devel-su
    # password

    mount --bind /proc /home/nemo/sid/proc
    mount --bind /sys /home/nemo/sid/sys
    mount --bind /dev /home/nemo/sid/dev
    mount --bind /dev/pts /home/nemo/sid/dev/pts
    cp /etc/resolv.conf /home/nemo/sid/etc/
    chroot /home/nemo/sid/
    apt-get update

Samstag, 19. April 2014

My N9 apps and games now self-hosted and on openrepos.net

As the versions of my apps on Nokia Store grow more and more outdated, I've decided to remove the apps from there and instead self-host them on my web page and/or put them on OpenRepos.net instead. A handful of apps stay on Nokia Store; those are mostly S40 apps or apps for which a Symbian version also exists.

Here's a list of my N9 apps that you can now get for free ("deb download" is as of posting this, for new versions visit the webpage or openrepos.net):
And here's a list of my N9 games that you can now get for free (again, "deb download" is as of posting this; new versions on the webpage or openrepos.net):
Of these, the "greatest hits" and most useful apps/most fun games are (in my opinion): Billboard, Volume+ As Camera Button and chro.mono, but also give qw The Game and Petals a try :)

A list of all my apps on OpenRepos.net is also available.

Please note that unless otherwise noted, do not copy the .deb files and distribute them yourself, please always link to the project webpage (the page, not the file) or the openrepos.net page - this makes sure users can always download the latest version and from a known-good source (always be careful when downloading and installing .debs from random webpages). For end users who want to stay up to date and install the packages comfortably, the Warehouse client for OpenRepos.net is recommended.

For some of these apps (not games) that are not open source yet, I plan to clean up and publish the source at some point in the future, so interested developers can have a look, add features and/or port it to new platforms.

Samstag, 15. März 2014

gPodder 4.0.0 for Sailfish OS released

While we're supplying N9 users with fresh releases of gPodder 3 regularly (the latest version, 3.6.1, has been released last week, and the update is available on gpodder.org/downloads), of course we've also been busy working on a newer, Qt 5 and PyOtherSide-based version of gPodder. After weeks of testing, I think it's good enough for a first release now, so let's warmly welcome gPodder 4.0.0 to the world of Sailfish apps. You can download it and its dependencies from the gPodder downloads page.

If you haven't read last year's article about Python and Qt 5, now might be a good time to do so. PyOtherSide is a much more minimalistic approach to Python bindings, and - in my obviously biased opinion - works better for gluing together a QML UI with a Python backend. In fact, it lends itself to clearly splitting the frontend from the backend, and with the "asynchronous by default" design, you have to work really hard to block your UI thread with long-running Python code (or multithreaded Python code that's waiting for the GIL to be released). PyOtherSide these days is also well-documented, and some early annoyances and bugs have been fixed with recent releases in February. In combination with Qt 5 and Python 3, it works well on OS X, Blackberry 10, Linux, Sailfish OS and Windows. With Qt 5.2 having official support for Android, and a Python 3 port being available, it's only a matter of time before PyOtherSide lands on Android.

For all Sailfish OS users out there: Until the next Sailfish OS update, you might have to install some dependencies before gPodder will correctly start up, these are:
  1. libpython3 (the Python 3 interpreter)
  2. python3-base (the Python 3 standard library)
  3. pyotherside (the Qt 5-Python3 bindings)
As these links point to the current version in OBS, they will break once one of these packages is updated. In this case, just look into the home:thp:gpodder armv7hl repository for the latest versions of these packages. With the next Sailfish OS update, recent-enough packages of PyOtherSide should be in the repositories, so you don't need to install the dependencies manually.

Donnerstag, 18. Juli 2013

The way forward with Python on Qt 5 (with a bit of history)

As you have probably heard on Twitter from the official Jolla account, the first Jolla will ship with Wayland. In that discussion, some worries are brought up about Python support with Qt 5. Here are my personal thoughts of how I see mobile Python development moving forward with the new technology. So first some background information:

If you want to write Python applications on a mobile platform, you usually need some way to interface with the native graphical toolkit. On Maemo 4 and Maemo 5, this was done via PyGTK and some additional bindings for the Hildon UI elements. PyGTK is licensed under the terms of the GNU LGPL, so you could use it to develop open and closed source applications.

When MeeGo 1.2 Harmattan (the system running on the N9) came up, the toolkit with which third party application developers wrote their applications changed from Gtk to Qt (or to be more precise, QML). Just like pure Gtk didn't provide all the UI elements for mobile (and therefore Hildon was used on top of Gtk), pure QML provided very little, and so Qt Quick Components were used for the Harmattan user interface (that includes things like buttons, toolbars, menus, dialogs, etc...). These could only be used from QML, so you had to use QML if you wanted your application to look anything like native (ignoring the MeeGo Touch Framework here, as it wasn't really used for third party apps, with the notable exception of sociality-mtf).

With Qt and QML being the new toolkit of choice, Python developers needed a different "bridge" to go from the Python world to the toolkit world. PyQt already existed for some time then, it had two license options: GNU GPL (basically requiring all your code that uses PyQt to also become GPL'd) and commercial. As the Wikipedia article of PySide says, after Nokia failed to reach an agreement with Riverbank Computing (the developers of PyQt) to change the license to LGPL, they started their own Python bindings named "PySide", which are licensed under the terms of the GNU LGPL. There's a wiki page with PySide-PyQt differences on the Qt Wiki.

As MeeGo at Nokia was ramped down, the core team of PySide had less and less time to spend on improving it. PySide is still kept up to date (the latest release is from July 9, 2013), and it already supports Python 3, but nobody has yet ported it to Qt 5. The most recent post I found about PySide and Qt 5 from one of the core developers was from November 2011. For using Python with Qt 5, there are two possibilities at the moment: Use PyQt under the terms of the GNU GPL, or get a commercial PyQt license. Another option to get LGPL'd bindings would be to add support for Qt 5 to PySide, but that needs somebody to adapt PySide to work with Qt 5.

But let's step back a little now. In my experience with writing Python applications for mobile operating systems (starting with Maemo 4 all the way to MeeGo 1.2 Harmattan), there are really two parts: The frontend, which is specified in terms of whatever the toolkit of the day happens to be, and the backend, which (in case of Python applications) is written in Python.

In Qt 5 / QtQuick, the user interface language is QML, which includes a declarative language and JavaScript for scripting. Writing QtQuick applications with Python doesn't mean replacing JavaScript, it means replacing the "backend" - code that would usually be written in C++ and take care of storage and communication. In fact, as QML grew more powerful over the years, you had to "go down to the C++ level" (or Python in our case) less often. And compared to imperative user interfaces (where you still go through your tree of UI widgets with code and then set some button text to some value), in declarative user interfaces the backend really only provides services to the frontend (that button gets the label from the backend in this way, and when the data in the backend changes, so does the button's text).

So, what we really need is a way to provide services to the QML UI by using Python. There's no need for having access to all classes in all Qt modules - even in Qt4 / PySide times, the classes that I used most often were QApplication (for the event loop), QDeclarativeView (for displaying the QML UI) and QObject (for providing signals and slots to interface the backend to the view). Things like access to the contacts database or GPS location can usually be done directly from QML (and displayed there) - it might be that you don't even need to have that data in your backend (and if you do, the UI can for example send the phone number of the contact "down to" the Python backend for further processing).

But before I'll tell you where I think mobile Python development should be heading, let's bring up yet another disadvantage of using PySide (probably applies to PyQt as well) for mobile applications: Startup time and responsiveness. Startup time is slow, because PySide libraries are big (they are - at least on the N9 - bigger than the Qt libraries they are binding), and the dynamic linker has to resolve all Qt symbols at load time instead of just the ones you end up using. Instead of seeing the QML UI right away, your application first has to load up the Python interpreter, then load up the PySide modules and other modules and then finally load and display the QML UI. And because of Python's Global Interpreter Lock, function calls from QML to Python will make the UI block and not be totally responsive, even if you are using threads.

So, in my opinion, mobile Python applications with Qt 5 have to be fast to load, lightweight and responsive. Fast to load can be achieved by making sure the libraries that get loaded are small, and that ideally the QML UI can already be displayed before the Python interpreter is even loaded and/or initialized. Lightweight can be achieved by not binding all the Qt classes, but only the ones you really need for creating QML applications. And responsive can be achieved by making sure that the interface between QML and Python is asynchronous, so the UI never blocks even if Python is working hard in the background to fulfill the requests of the user interface.

I currently have a prototype running on Python 3.3 (Python 2.6 and 2.7 is also supported still, but for me, this is a great opportunity to migrate some of my old code to Python 3, and new code should be written in Python 3, because, as you know, Python 2 sucks) and Qt 5 (I've just ported it to Qt 5 today, but it runs just as well on Qt 4 - with all the advantages like startup time and asynchronousness still being valid). In some unscientific tests that I carried out a few weeks ago, I've brought down startup time of gPodder on the N9 down from ~ 12 seconds (using Python 2 and PySide) to ~ 3 seconds (using Python 3.3 and the lightweight "PyOtherSide" approach). No code release yet, as this is tailored towards my gPodder experiment right now and has many things hardcoded, but once I clean it up (maybe as a QML plugin) and define the API in more detail, I plan to release it.

In the mean time, here are some (relatively old) videos of PyOtherSide running with Python 3 and Qt 4 on MeeGo Harmattan, Blackberry 10 and Android (so yes, it is portable).