xrdp läuft zur Zeit recht stabil auf FreeBSD

Nur eine kleine Empfehlung. Immer wieder habe ich mir xrdp auf FreeBSD angesehen. Bisher empfand ich es als unbrauchbar, da es alles andere als stabil funktionierte und teils auch nicht startete.

Seit einer Weile funktioniert xrdp hervorragend.

FreeBSD-Desktop Xfce auf xrdp unter FreeBSD 14.2

xrdp stellt eine RDP-Session her. Damit kann man einen entfernten Computer via a nutzen, ähnlich zu RDP unter Windows. Auf dem oberen Bild kann man das sehen. Hier läuft auf einem Host ein FreeBSD 14.2, worunter ein Jail mit FreeBSD 14.2 läuft, auf dem xrdp installiert ist. Darin wird dann ein Xfce ausgeführt. Seit gut drei oder vier Monaten läuft es problemfrei.

Auf macOS nutze ich Jump Desktop, um auf den xrdp-Desktop zuzugreifen. Ich hatte auch Windows App ausprobiert, hier ist aber das Problem, dass ich es nie geschafft habe, ein deutsches Tastaturlayout zu übertragen. Mit Jump Desktop, welches leider kostenpflichtig ist, ist dies problemlos möglich.

Jump Desktop Hauptfenster

Ich kann das bisher sehr empfehlen, wenn man kein FreeBSD auf seinem Computer installieren will, sondern eine zentrale Installation nutzen möchte, auf der sich auch verschiedene Nutzer gleichzeit einloggen möchten.

Gebrauchte Macs an Apple-Account gebunden

Vielleicht hilft das dem ein oder anderen. Ich habe 2023 gebraucht ein MacBook Pro 16″ (BJ 2019) als Zweitgerät auf Kleinanzeigen gekauft. Zwei Jahre lang hatte ich das in Benutzung, jetzt übergab ich es, aufgrund eines neuen Hardwarekaufs, an meinen Sohn. Also Programme, die er nicht braucht, gelöscht, Account angelegt und meinen Account gelöscht. So weit, so gut. Er hatte das Gerät jetzt gut eine Woche in Benutzung und forderte mich gestern, am 13.11.2025 auf, ihm nochmal das WLAN-Passwort zu geben, denn sein Mac sei irgendwie abgestürzt.

Ich sah mir das Gerät direkt einmal an und er hatte recht: Es versuchte, in den Internet-Recovery-Modus zu booten. Mir fiel dann ein, dass beim Erstellen vom Account meines Sohns irgendeine Apple-ID angezeigt wurde, die vom Vorbesitzer war. Anscheinend ist dieser informiert worden, dass da an seinem alten Mac gearbeitet wird. Er hat dann – so wie es aussieht – den Mac aus der Ferne vollständig gelöscht.

Zusätzlich bin ich nicht mehr in der Lage, den Mac überhaupt noch zu nutzen, da dieser via Apple-ID des Vorbesitzers entsperrt werden muss. Ich habe ihm einen Brief geschrieben (ich habe nur noch die Adresse und hoffe, er wohnt da noch) und darum gebeten, den Mac aus seinem Account zu entfernen. Wenn er das nicht macht, habe ich nur rechtliche Möglichkeiten, das zu verlangen, die aber wiederrum mit Aufwand und ggf. Kosten einhergehen. Ansonsten ist das Gerät nicht mehr als ein teurer Briefbeschwerer.

Mein Sohn ist natürlich enttäuscht und traurig. Ich hoffe, dass der Vorbesitzer noch einlenkt und alles wieder gut wird.

Also: Denkt beim Verkauf von Apple-Geräten daran, sie aus euren Accounts freizugeben und wenn ihr solche Geräte kauft, checkt das vorher. Ich denke nicht, dass hier böse Absicht hinter gesteckt hat, weiß es aber nicht. Natürlich sind alle Daten, Programme, usw. unwiderruflich gelöscht. Wohl dem, der ein Backup hat.

PostgreSQL-Installation oder -Update auf FreeBSD läuft auf Fehler: >>could not load locale „sr_YU.ISO8859-5″<<

Nachdem ich PostgreSQL 16 auf Version 17 aktualisieren wollte, trat beim initdb folgender Fehler auf:

root@marge:~ # service postgresql oneinitdb
initdb postgresql
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with this locale configuration:
  locale provider:   libc
  LC_COLLATE:  C
  LC_CTYPE:    C.UTF-8
  LC_MESSAGES: C.UTF-8
  LC_MONETARY: C.UTF-8
  LC_NUMERIC:  C.UTF-8
  LC_TIME:     C.UTF-8
The default text search configuration will be set to "english".

Data page checksums are disabled.

fixing permissions on existing directory /server/database/postgres ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default "max_connections" ... 100
selecting default "shared_buffers" ... 128MB
selecting default time zone ... UTC
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... 2025-08-25 11:53:19.740 UTC [71247] FATAL:  could not load locale "sr_YU.ISO8859-5.bak"
2025-08-25 11:53:19.740 UTC [71247] STATEMENT:  SELECT pg_import_system_collations('pg_catalog');

child process exited with exit code 1
initdb: removing contents of data directory "/server/database/postgres"

Die einzige Möglichkeit, wie ich das Problem umgehen konnte, war, dass ich die Datei „/usr/share/locale/sr_YU.ISO8859-5“ in ein anderes Verzeichnis verschoben habe und dann initdb erneut ausführte. Danach habe ich die Datei zurückverschoben.

Testaufgaben nach einem Bewerbungsgespräch

Ich habe jetzt länger überlegt, ob ich darüber überhaupt bloggen möchte. Ich mache es doch einmal, denn es gab für mich etwas sehr interessantes und es ist schon eine ganze Weile her.

Ich war auf Jobsuche, schrieb hier und da eine Bewerbung. Dann hatte ich ein Vorstellungsgespräch und der Herr am anderen Ende der Telefonleitung meinte, es könnte passen. Nachdem er über meine Konditionen nachdachte und das mit der Geschäftsleitung durchsprach, bekam ich einen Anruf, dass diese passen würden, die Firma aber eine Testaufgabe für mich hätte. Darum soll es hier gehen, nämlich um diese eine Testaufgabe und um Testaufgaben im Allgemeinen.

Nachdem wir den Techstack abgeklärt hatten und es darum um PHP und Symfony ging und ich auch sagte, dass ich von beidem keine Ahnung habe (PHP habe ich das letzte Mal vor 20 Jahren gemacht), wurde mir versichert, dass vor dem persönlichen Kennenlernen nur noch eine einfache Testaufgabe zu meistern wäre, nämlich die Umsetzung eines Kontonummer-Bankleitzahlen-IBAN-Konverters. Ich sagte: kein Problem, das musste ich auch schon in einem echten Projekt vor einigen Jahren machen, er soll mir die Aufgabe zukommen lassen. Kurze Zeit später war dann das Dokument mit der Testaufgabe da. Fünf vollgeschriebene DinA4-Seiten. Dabei ging es nicht um die Entwicklung eines Konverters, sondern um ein gesamtes Projekt mit etlichen Zwischenstationen.

Ich sollte mich erst einmal in PHP 8.1 einarbeiten. Danach in Symfony 6.1. Um beides richtig zu machen, würde ich schon keine Stunden mehr veranschlagen, sondern eher einige Tage bishin zu wenigen Wochen. Eine Programmiersprache lerne ich zwar schnell, vor allem PHP, bei der ich nicht einmal von ganz vorne einsteigen müsste, dann aber noch ein komplexes Framework, das ist Aufwand.

Zusätzlich dazu sollte ein Login-Verfahren entwickelt werden und eine dazugehörige Website mit Datenbankanbindung (MySQL oder MariaDB). In der Datenbank sollten die bereits zuvor konvertierten Daten tabellarisch dargestellt werden. Jeder Aufruf sollte einen Zähler inkrementieren.

Weiterhin sollten die Daten dann als JSON exportiert werden können.

Dazu gehörte dann die Installation und Konfiguration von Debian 11, die Installation der benötigten Software (spezielle PHP-Version, nicht in APT drin, MySQL oder MariaDB, Symfony und Apache oder NGINX).

Das war der technische Part. Dazu kam dann ein Benutzerhandbuch für den Konverter über Funktionalität und Bedienung. Dann noch eine Dokumentation für das gesamte Deployment durch einen Laien (Installation und Konfiguration Betriebssystem, Installation und Konfiguration Webserver, Installation und Konfiguration Datenbankserver, Installation und Konfiguration PHP, Installation und Konfiguration von Symfony – durch einen Laien!).

Weiterhin sollte das Projekt vorher komplett durchgeplant werden, vermute mit Pflichenheft, war nicht genauer beschrieben und es sollten auch Tests (Unit-Tests, händische Tests) durchgeführt und dokumentiert werden.

Versteht mich bitte nicht falsch: Das alles kann ich, aber rein realistisch, wenn man es vernünftig macht, ist das ein Projekt, das man nicht in einigen Stunden hinbringt, selbst, wenn man PHP und Symfony auf hohem Level beherrscht. Schon alleine am Pflichtenheft schreibt man eine Weile, das Benutzerhandbuch rotzt man auch nicht einfach runter…

Ich lehnte den Job dann mit Begründung ab. Eine Antwort darauf erhielt ich nie.

Unterschiede zwischen Qt und wxWidgets

Qt und wxWidgets sind zwei beliebte Frameworks für die Entwicklung von Benutzeroberflächen, die sich in technischen und Lizenzunterschieden unterscheiden. Technisch gesehen basiert Qt auf einer objektorientierten Architektur mit einem eigenen Meta-Objekt-System (MOC), das Funktionen wie Signale und Slots für die Ereignisbehandlung bietet. Im Gegensatz dazu verwendet wxWidgets eine Wrapper-Architektur, die die nativen APIs der Zielplattform nutzt, was zu Anwendungen führt, die sich stärker an das native Look-and-Feel anpassen. In Bezug auf Programmiersprachen unterstützt Qt hauptsächlich C++, bietet aber auch Bindings für Sprachen wie Python (PyQt/PySide). wxWidgets ist primär für C++ konzipiert, hat aber ebenfalls Bindings für Python (wxPython) und Perl. Beim GUI-Design kommt Qt mit dem leistungsstarken Qt Designer zur visuellen Gestaltung von Benutzeroberflächen, während wxWidgets oft manuelles Codieren der GUI oder den Einsatz von Drittanbieter-Tools erfordert. In Bezug auf die Plattformunterstützung deckt Qt Windows, macOS, Linux sowie Embedded-Systeme und mobile Plattformen (Android, iOS) ab. wxWidgets unterstützt ebenfalls Windows, macOS und Linux, hat aber eingeschränkte Unterstützung für mobile Geräte.

In Bezug auf die Lizenzierung ist Qt unter GPL, LGPL und kommerziellen Lizenzen lizenziert. Die GPL erfordert die Offenlegung des Quellcodes von abgeleiteten Werken, während die LGPL eine flexiblere Nutzung ermöglicht, insbesondere für proprietäre Software, wenn dynamische Verlinkung verwendet wird. Unternehmen können auch eine kommerzielle Lizenz erwerben, um diese Einschränkungen zu umgehen. wxWidgets wird unter der wxWidgets-Lizenz veröffentlicht, die sehr liberal ist und ähnlich wie die LGPL funktioniert. Sie erlaubt die Entwicklung von sowohl Open-Source- als auch proprietärer Software, ohne die Notwendigkeit, den Quellcode offenzulegen.

Zusammenfassung der Unterschiede

  • Architektur:
    • Qt: Objektorientiert mit MOC, Signale und Slots.
    • wxWidgets: Wrapper-Architektur, nutzt native APIs.
  • Programmiersprachen:
    • Qt: Hauptsächlich C++, Bindings für Python und andere.
    • wxWidgets: Primär C++, Bindings für Python und Perl.
  • GUI-Design:
    • Qt: Qt Designer für visuelle Gestaltung.
    • wxWidgets: Oft manuelles Codieren oder Drittanbieter-Tools.
  • Plattformunterstützung:
    • Qt: Windows, macOS, Linux, Embedded, mobile (Android, iOS).
    • wxWidgets: Windows, macOS, Linux, eingeschränkte mobile Unterstützung.
  • Lizenz:
    • Qt: GPL, LGPL, kommerziell.
    • wxWidgets: Liberale wxWidgets-Lizenz, ähnlich LGPL.

wxWidgets-Tutorial 014: Überblick Event-Handling

In diesem Video zeige ich grob, wie das Event-Handling funktioniert. Tiefer gehen wir darauf aber dann bei den einzelnen Widgets ein.

Letztlich gibt es drei Varianten, wovon die dritte „deprecated“ ist und nicht mehr in neuen Programmen benutzt werden soll:

  1. Event-Table
  2. Bind
  3. Connect
Überblick über Event-Handling

Ein Screenshot unseres Beispielprogramms.

Screenshot unseres Beispielprogramms

Der Quelltext:

#include <wx/wx.h>

class MyApp : public wxApp {

	public:
		bool OnInit();

};

class MyFrame : public wxFrame {

	public:
		MyFrame();
	
	protected:
		void OnButtonClick(wxCommandEvent &event);

};

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit() {
	auto *myFrame = new MyFrame;
	myFrame->Show();

	SetTopWindow(myFrame);

	return true;
}

MyFrame::MyFrame() : wxFrame(nullptr, wxID_ANY, _("Events")) {
	wxButton *button = new wxButton(this, wxID_ANY, _("Click me"));

	button->Bind(wxEVT_BUTTON, &MyFrame::OnButtonClick, this);
}

void MyFrame::OnButtonClick(wxCommandEvent &event) {
	std::cout << "Button clicked" << std::endl;
}

Hier geht es zum Video.

wxWidgets-Tutorial 013: wxGridBagSizer

In diesem Video zeige ich, wie ein wxGridBagSizer funktioniert.

Der wxGridBagSizer basiert auf dem wxFlexGridSizer, bietet aber die Möglichkeit, Widgets und Sizer darin explizit zu positionieren und der Sizer kann Zellen zusammenführen.

Sizer (Layoutmanager): wxGridBagSizer

Hier ein Screenshot von unserem Beispielprogramm:

Screenshot des Beispielprogramms

Der Quelltext:

#include <wx/wx.h>
#include <wx/gbsizer.h>

class MyApp : public wxApp {

	public:
		bool OnInit();

};

class MyFrame : public wxFrame {

	public:
		MyFrame();

};

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit() {
	auto *myFrame = new MyFrame;
	myFrame->Show();

	SetTopWindow(myFrame);

	return true;
}

MyFrame::MyFrame() : wxFrame(nullptr, wxID_ANY, _("wxGridBagSizer")) {
	auto *mainPanel = new wxPanel(this);
	
	auto *gridBagSizer = new wxGridBagSizer(5, 5);

	wxGBSpan twoSpan(1, 2);

	gridBagSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Firstname:")), wxGBPosition(0, 0), wxDefaultSpan, wxALIGN_RIGHT);
	gridBagSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), wxGBPosition(0, 1), twoSpan, wxEXPAND);

	gridBagSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Lastname:")), wxGBPosition(1, 0), wxDefaultSpan, wxALIGN_RIGHT);
	gridBagSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), wxGBPosition(1, 1), twoSpan, wxEXPAND);

	gridBagSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Age:")), wxGBPosition(2, 0), wxDefaultSpan, wxALIGN_RIGHT);
	gridBagSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), wxGBPosition(2, 1));

	gridBagSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Zipcode/City:")), wxGBPosition(3, 0), wxDefaultSpan, wxALIGN_RIGHT);
	gridBagSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), wxGBPosition(3, 1));
	gridBagSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), wxGBPosition(3, 2), wxDefaultSpan, wxEXPAND);

	gridBagSizer->AddGrowableCol(2);

	mainPanel->SetSizer(gridBagSizer);
	gridBagSizer->SetSizeHints(this);
}

Hier geht es zum Video.

wxWidgets-Tutorial 012: wxSizer verschachteln

In diesem Video zeige ich, wie einfach es ist, wxSizer zu verschachteln.

Um komplexe GUIs zu entwickeln, ist es unabdingbar, dass man Sizer ineinander verschachteln kann. Damit lassen sich einfach und übersichtlich Formulare entwickeln, die dem Benutzer einen hohen Mehrwert bringen.

Sizer (Layoutmanager): wxSizer verschachteln

Hier ein Screenshot unseres Programms:

Screenshot von verschachtelten Sizern

Hier der Quelltext:

#include <wx/wx.h>

class MyApp : public wxApp {

	public:
		bool OnInit();

};

class MyFrame : public wxFrame {

	public:
		MyFrame();

};

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit() {
	auto *myFrame = new MyFrame;
	myFrame->Show();

	SetTopWindow(myFrame);

	return true;
}

MyFrame::MyFrame() : wxFrame(nullptr, wxID_ANY, _("Sizer in Sizers")) {
	auto *mainPanel = new wxPanel(this);

	auto *mainBoxSizer = new wxBoxSizer(wxHORIZONTAL);

	auto *flexGridSizer = new wxFlexGridSizer(2, 5, 5);

	flexGridSizer->AddGrowableCol(1);
	flexGridSizer->AddGrowableRow(4);

	flexGridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Firstname:")), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
	flexGridSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), 0, wxEXPAND);

	flexGridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Lastname:")), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
	flexGridSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), 0, wxEXPAND);

	flexGridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Zipcode/City:")), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);

	auto *addressSizer = new wxBoxSizer(wxHORIZONTAL);
	addressSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY));
	addressSizer->AddSpacer(5);
	addressSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), 1);

	flexGridSizer->Add(addressSizer, 0, wxEXPAND);

	flexGridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Age:")), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
	flexGridSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY));

	flexGridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Informations:")));
	flexGridSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), 0, wxEXPAND);

	mainBoxSizer->Add(flexGridSizer, 1, wxEXPAND | wxALL, 15);

	mainPanel->SetSizer(mainBoxSizer);
	mainBoxSizer->SetSizeHints(this);
}

Hier geht es zum Video.

wxWidgets-Tutorial 011 – wxFlexGridSizer

In diesem Video zeige ich die Verwendung von wxFlexGridSizer.

Im Gegensatz zum wxBoxSizer, der Elemente nur horizontal oder vertikal ausrichten kann, dient der wxFlexGridSizer wie eine Tabelle, in der bestimmt werden kann, welche Spalten und Zeilen sich ausdehen durfen und in dem man einfach Formulare zusammensetzen kann.

Sizer (Layoutmanager): wxFlexGridSizer

Hier ein Screenshot unseres Programms:

Screenshot vom wxFlexGridSizer

Hier der Quelltext:

#include <wx/wx.h>

class MyApp : public wxApp {

	public:
		bool OnInit();

};

class MyFrame : public wxFrame {

	public:
		MyFrame();

};

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit() {
	auto *myFrame = new MyFrame;
	myFrame->Show();

	SetTopWindow(myFrame);

	return true;
}

MyFrame::MyFrame() : wxFrame(nullptr, wxID_ANY, _("wxFlexGridSizer")) {
	auto *mainPanel = new wxPanel(this);

	auto *flexGridSizer = new wxFlexGridSizer(2, 5, 5);

	flexGridSizer->AddGrowableCol(1);
	flexGridSizer->AddGrowableRow(3);

	flexGridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Firstname:")), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
	flexGridSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), 0, wxEXPAND);

	flexGridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Lastname:")), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
	flexGridSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), 0, wxEXPAND);

	flexGridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Age:")), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
	flexGridSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY));

	flexGridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Informations:")));
	flexGridSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), 0, wxEXPAND);

	mainPanel->SetSizer(flexGridSizer);
	flexGridSizer->SetSizeHints(this);
}

Hier geht es zum Video.