Skip to content

Sunday, 31 March 2024

Fedora Logo
Μαζί με το openSUSE, το Arch και το Debian, το Fedora είναι μία από τις «τέσσερεις μεγάλες» διανομές Linux. Ανάγεται η καταγωγή του στο Red Hat Linux, την αρχική διανομή που βασίζεται σε RPM.

Το Fedora είναι γνωστό για τις τεχνολογίες αιχμής, το πιο πρόσφατο λογισμικό και τις συχνές ενημερώσεις. Είναι επίσης μια από τις λίγες μεγάλες διανομές που αγκαλιάζουν το GNOME vanilla. Τον τελευταίο καιρό, η διανομή έχει γίνει πιο φιλική προς το χρήστη, με οθόνη καλωσορίσματος, υποστήριξη Flatpak και δυνατότητα ενεργοποίησης αποθετηρίων τρίτων κατά τη ρύθμιση.

Ιστορία του Fedora

Η ιστορία του Fedora είναι παράλληλη από την ιστορία του Red Hat. Η διανομή ήταν αρχικά γνωστή ως "Fedora Linux", στη συνέχεια "Fedora Core", πριν τελικά καταλήξει στο Fedora.

Το Fedora Linux ήταν ένα αποθετήριο τρίτου κατασκευαστή για το αρχικό Red Hat Linux, ενώ το Fedora Core ήταν μια δωρεάν έκδοση του Red Hat Enterprise Linux που διατηρείται από την κοινότητα. Σήμερα, το Fedora είναι upstream για το Red Hat Enterprise Linux και χρησιμεύει ως μια καλή προεπισκόπηση του τι πρόκειται να γίνει στην "πληρωμένη" έκδοση Workstation.

Ενώ είναι κοινοτικό έργο, το Fedora προφανώς χρηματοδοτείται από τη Red Hat, η οποία ανήκει πλέον στην IBM.

Αξιοσημείωτα χαρακτηριστικά του Fedora

Με μια τόσο μακρά ιστορία, τα επιτεύγματα του Fedora είναι αμέτρητα. Ευτυχώς, αυτή η διανομή έχει πολλά πράγματα αυτή τη στιγμή, επομένως δεν υπάρχει λόγος να επιστρέψουμε στην αρχαία ιστορία για τα κυριότερα σημεία.

1. Κυκλοφορεί με το αυθεντικό GNOME ως προεπιλογή

Ένα από τα μεγαλύτερα πλεονεκτήματα του Fedora είναι η κυκλοφορία του με το αυθεντικό GNOME.

Θα νομίζατε ότι επειδή το GNOME είναι ένα από τα "δύο μεγάλα" περιβάλλοντα επιφάνειας εργασίας Linux και είναι τόσες πολλές διανομές που το χρησιμοποιούν, ότι το GNOME θα ήταν κοινό για όλους, αλλά όχι. Σήμερα, οι περισσότερες διανομές που κυκλοφορούν με το GNOME περιλαμβάνουν πολλές τροποποιήσεις που προσπαθούν να κάνουν το GNOME να συμμορφώνεται με τις συμβάσεις GUI της δεκαετίας του '90.

Το Fedora δεν το κάνει αυτό. Αντίθετα, παρέχει ένα από τα πιο "καθαρά" και πιο ενημερωμένα παραδείγματα του GNOME.

2. Φιλικό προς το χρήστη

Ενώ πολλές διανομές ξεπερνούν τα όρια για να βοηθήσουν τους νέους χρήστες με προγράμματα εγκατάστασης γραφικών και οθόνες καλωσορίσματος, κανείς δεν περίμενε πραγματικά να κάνει κάτι αντίστοιχο το Fedora. Αλλά το έκανε. Το Fedora υποστηρίζει το Flatpak out-of-the-box εκτός από τα αρχεία RPM.

Επιπλέον, μπορείτε να ενεργοποιήσετε τα αποθετήρια τρίτων κατά τη διάρκεια της εγκατάστασης. Κατά την εγκατάσταση, σας υποδέχεται μια χρήσιμη, γραμμική οθόνη καλωσορίσματος που εξηγεί τα βασικά στοιχεία της διεπαφής χρήστη, τις χειρονομίες και τις συντομεύσεις.

3. Προσφέρει λογισμικό αιχμής

Το Fedora ενημερώνεται κάθε έξι μήνες, δεν έχει έκδοση LTS, ώστε να λαμβάνετε πάντα τις πιο πρόσφατες ενημερώσεις και η πιο πρόσφατη έκδοση είναι πάντα η κορυφαία έκδοση.

Πέρα από τις συχνές αναβαθμίσεις, το Fedora κερδίζει τους περισσότερους αντιπάλους του στη κυκλοφορία ως προεπιλεγμένο λογισμικό αιχμής, ανοιχτού κώδικα. Ήταν η πρώτη μεγάλη διανομή που άλλαξε από το X11 στο Wayland και από το PulseAudio στο PipeWire.

4. Το Fedora είναι αξιόπιστο

Δεν βλέπετε συχνά μια διανομή να είναι ταυτόχρονα αξιόπιστη με λογισμικό αιχμής.

Όταν το Fedora εισάγει θεμελιώδεις αλλαγές, όπως τα παραπάνω παραδείγματα, είναι καλό σημάδι ότι αυτές οι τεχνολογίες είναι επιτέλους ώριμες. Τότε βλέπετε άλλες διανομές που ακολουθούν αργά.

Αν θέλετε να ζείτε στα άκρα, υπάρχουν εκδόσεις ανάπτυξης του Fedora όπως το Rawhide.

Εκδόσεις Fedora

Το Fedora προσφέρει τρεις κανονικές εκδόσεις και τρεις επίσημες "αναδυόμενες εκδόσεις". Ωστόσο, μόνο δύο από αυτά τα έξι είναι για καθημερινή χρήση από έναν τελικό χρήστη. Θα παραλείψουμε τις "γεύσεις" που εστιάζονται σε διακομιστή και σε IoT.

1. Workstation

Fedora Workstation
Το Fedora Workstation είναι η κορυφαία έκδοση του έργου. Διαθέτει την τελευταία τρέχουσα σταθερή έκδοση GNOME και υποστήριξη Flatpak out-of-the-box.

Λήψη: Workstation Fedora (δωρεάν & ανοιχτού κώδικα)

2. Silverblue

Η αναδυόμενη έκδοση Fedora Silverblue είναι μια "αμετάβλητη" παραλλαγή του Fedora Workstation. Η κύρια διαφορά είναι ότι οι χρήστες είναι πιθανό να αντιμετωπίσουν προβλήματα με την εγκατάσταση RPM, καθώς το Flatpak είναι η εγγενής μορφή πακέτου του Silverblue.

Λήψη: Fedora Silverblue (δωρεάν & ανοιχτού κώδικα)

3. Kinoite

Το Kinoite είναι μια ανερχόμενη έκδοση του Fedora που δεν εμφανίζεται ακόμα στην αρχική σελίδα. Το Kinoite είναι απλώς μια εναλλακτική λύση με "γεύση" KDE Plasma εναλλακτικό του Silverblue.

Λήψη: Fedora Kinoite (δωρεάν & ανοιχτού κώδικα)

4. Sericea

Ο δημοφιλής διαχειριστής παραθύρων πλακιδίων Sway προσφέρεται από τη Fedora Sericea με "αμετάβλητο" τρόπο. Κάνει το Sway προσιτό και ενδιαφέρον τόσο στους νέους όσο και στους έμπειρους χρήστες που προτιμούν να μην αλληλεπιδρούν με το περιβάλλον τους μέσω ποντικιού, επιφάνειας αφής ή άλλης συσκευής κατάδειξης. Το Fedora Sericea προσφέρει μια ολοκληρωμένη εμπειρία με μια απλή διεπαφή χρήστη και περιλαμβάνει ελαφριές εφαρμογές για περιήγηση στο web, σύνταξη κειμένου και αναπαραγωγή πολυμέσων.

Λήψη: Fedora Kinoite (δωρεάν & ανοιχτού κώδικα)

Fedora Spins

Όπως πολλές διανομές, το Fedora προσφέρει μια ποικιλία εναλλακτικών λύσεων με μια ποικιλία περιβαλλόντων επιφάνειας εργασίας. Το Fedora ονομάζει αυτές τις παραλλαγές "Spins".

1. KDE Plasma Desktop

Fedora KDE Plasma
Το KDE Plasma Spin του Fedora αφήνει ανέπαφες τις περισσότερες προεπιλογές του KDE, αλλάζοντας μόνο την ταπετσαρία και το εικονίδιο εκκίνησης εφαρμογών και επιτρέποντας το διπλό κλικ για άνοιγμα/εκκίνηση.

Λήψη: Fedora KDE Plasma Desktop Spin (δωρεάν & ανοιχτού κώδικα)

2. XFCE Desktop

Fedora XFCE
Το Fedora XFCE Spin χρησιμοποιεί την παραδοσιακή διάταξη διεπαφής τύπου BSD/Mac. Φαίνεται πολύ ωραίο για ένα ελαφρύ περιβάλλον επιφάνειας εργασίας.

Λήψη: Fedora XFCE Desktop Spin (δωρεάν & ανοιχτού κώδικα)

3. Cinnamon Desktop

Fedora Cinnamon
Παραδόξως, το Fedora προσφέρει το Cinnamon, ένα προϊόν του Linux Mint.

Εδώ το Cinnamon διαθέτει την επωνυμία Fedora, μπλε χρώμα με έμφαση, λεπτή γραμμή εργασιών και λείπει κυρίως τα XApps του Mint. Παρά αυτές τις αλλαγές, είναι αναζωογονητικό να βλέπεις το Cinnamon να χρησιμοποιείται διαφορετικά από ό,τι στο Linux Mint.

Λήψη: Fedora Cinnamon Desktop Spin (δωρεάν & ανοιχτού κώδικα)

4. MATE-Compiz Desktop

Fedora MATE-Compiz
Κολλημένοι στο χρόνο; Το Fedora MATE-Compiz Spin είναι τέλειο για όσους λαχταρούν τις μέρες δόξας του GNOME 2 Ubuntu και τα εντυπωσιακά εφέ επιφάνειας εργασίας.

Λήψη: Fedora MATE-Compiz Desktop Spin (δωρεάν & ανοιχτού κώδικα)

5. i3 Tiling Window Manager

Fedora i3 Tiling Window Manager
Ναι, το Fedora έχει ακόμη και ένα tiling window manager spin, οπότε τώρα μπορείτε και εσείς να δημοσιεύετε στο r/unixporn. Πέρα από την πλάκα, το i3 είναι ένα από τα πιο δημοφιλή WM για πλακάκια και ένα τέλειο σημείο εκκίνησης για να μπείτε στον κόσμο των tiling WM που βασίζονται σε πληκτρολόγιο.

Αυτή η κατηγορία υπολογιστών προσφέρει ανώτερη απόδοση οθόνης, χαμηλότερο κόστος συστήματος και αυξημένη ταχύτητα αλληλεπίδρασης με τον χρήστη μέσω συντομεύσεων που βασίζονται στο πληκτρολόγιο.

Λήψη: Fedora i3 Tiling WM Spin (δωρεάν & ανοιχτού κώδικα)

6. Sway Tiling Window Manager

Fedora Sway Tiling Window Manager
Το Fedora Sway Spin παρέχει τον δημοφιλές διαχειριστή παραθύρου πλακιδίων Sway. Κάνει το Sway προσβάσιμο και ελκυστικό τόσο για αρχάριους όσο και για προχωρημένους χρήστες που προτιμούν να μην χρησιμοποιούν το ποντίκι, την επιφάνεια αφής ή άλλη συσκευή κατάδειξης για να αλληλεπιδρούν με το περιβάλλον τους. Διαθέτει ελαφριές εφαρμογές για περιήγηση στον Ιστό, επεξεργασία κειμένου και αναπαραγωγή πολυμέσων, το Fedora Sway Spin προσφέρει μια ολοκληρωμένη εμπειρία με ένα μινιμαλιστικό περιβάλλον χρήστη.

Λήψη: Fedora Sway Tiling WM Spin (δωρεάν & ανοιχτού κώδικα)

7. LXQt Desktop

Fedora LXQt Spin
Χωρίς να σταματά στα "μεγάλα τρία" περιβάλλοντα επιφάνειας εργασίας, το Fedora προσφέρει επίσης το LXQt. Αυτή η εναλλακτική λύση του LXDE που βασίζεται σε Qt παρέχει μια απλή εμπειρία που μοιάζει με τα Windows XP.

Λήψη: Fedora LXQt Desktop Spin (δωρεάν & ανοιχτού κώδικα)

8. Budgie Desktop

Fedora Budgie
Το Fedora Budgie Spin παρουσιάζει το Budgie Desktop, μια πλούσια σε χαρακτηριστικά, μοντέρνα επιφάνεια εργασίας. Αυτό το Spin έχει σχεδιαστεί για να ευθυγραμμίζεται στενά με το upstream Budgie Desktop, παρέχοντας μια εμπειρία σχεδόν βανίλια με ένα επιλεγμένο σύνολο προεπιλεγμένων εφαρμογών που ταιριάζουν καλύτερα με το Budgie.

Λήψη: Fedora Budgie Desktop Spin (δωρεάν & ανοιχτού κώδικα)

9. SOAS (Sugar on a Stick)

Fedora SOAS (Sugar on a Stick)
Αφού εξαντλήθηκαν όλα τα περιβάλλοντα επιφάνειας εργασίας που έχετε ακούσει, το Fedora συνεχίζει να εντυπωσιάζει με το Fedora spin SOAS. Ίσως το γνωρίζετε καλύτερα ως Sugar on a Stick, το οποίο όπως υποδηλώνει το όνομά του είναι Sugar on a bootable USB stick.

Το περιβάλλον επιτραπέζιου υπολογιστή πρώιμης εκμάθησης έγινε ευρέως γνωστό όταν επιλέχθηκε ως το λειτουργικό σύστημα για το έργο OLPC (Ένας φορητός υπολογιστής ανά παιδί). Λήψη: Fedora SOAS Spin (δωρεάν & ανοιχτού κώδικα)

10. Phosh

Fedora Phosh
Το Phosh spin παρέχει μια φιλική διεπαφή προς κινητά, βασισμένη στην αφή της οθόνης. Είναι κατασκευασμένο για φορητές συσκευές όπως τηλέφωνα και tablet, αλλά και φορητούς υπολογιστές με οθόνες αφής.

Το Phosh σάς επιτρέπει να χρησιμοποιείτε μια συσκευή που βασίζεται στην αφή για γρήγορη εκκίνηση και εναλλαγή εφαρμογών, καθώς και εύχρηστες ρυθμίσεις όπως το επίπεδο της μπαταρίας και η ισχύς του σήματος χαμηλής τάσης.

Πίσω από αυτήν την επιφάνεια εργασίας βρίσκεται ολόκληρη η συλλογή πακέτων Fedora τα οποία μπορείτε να εγκαταστήσετε και να χρησιμοποιήσετε όπως σας ταιριάζει. Το Phosh είναι χτισμένο σε wayland και άλλες σύγχρονες τεχνολογίες επιτραπέζιων υπολογιστών gnome.

Λήψη: Fedora Phosh Spin (δωρεάν & ανοιχτού κώδικα)

Σε ποιον απευθύνεται το Fedora;

Το Fedora δεν είναι μόνο μια πρωτότυπη διανομή, αλλά έχει γίνει και αρκετά φιλικό προς τον χρήστη τα τελευταία χρόνια. Αυτός είναι ένας σπάνιος συνδυασμός, καθώς το Arch αναμφισβήτητα δεν είναι φιλικό προς το χρήστη και το Debian μόλις πρόσθεσε ένα πρόγραμμα εγκατάστασης γραφικών πριν από μερικά χρόνια.

Εάν είστε λάτρης του GNOME, το Fedora είναι η μόνη μεγάλη διανομή που στέλνει μια ενημερωμένη έκδοση του GNOME vanilla. Οι χρήστες Mac και οι νεότεροι που μεγάλωσαν με κινητές συσκευές μπορεί επίσης να εκτιμήσουν το GNOME. Και οι χρήστες φορητών υπολογιστών θα ερωτευτούν τις χειρονομίες της επιφάνειας αφής Wayland για έλεγχο του χώρου εργασίας.

I didn’t work much on KDE for the first half of March, but still managed to squeeze out some good features and bugfixes. I’m also starting on the Wayland grant work I teased soon, so look forward to news on that next month.

Plasma

[Bugfix] The text and buttons are now centered in KCMs that launch external applications, such as the System Monitor in KInfoCenter. [6.1]

What the System Monitor KCM looks like now

[Bugfix] Fix numerous spacing and layout issues in the Date & Time KCM. [6.1]

Now it looks nice and centered, and there’s less “mystery spacing”

[Bugfix] Actually disable the calendar in the Date & Time KCM when NTP is enabled. Basically fixing the bug you see in the above screenshot where the date picker is still enabled. [6.1]

[Bugfix] (Haven’t been able to continue it yet) but disable the searchbox and filter actions when there’s no command output. If you don’t have the “aha” installed you shouldn’t be able to search for non-existent text under “Firmware Security”. [6.1]

[Bugfix] In the Bluetooth KCM, “game controllers” are now called as such where it was previously “joypads”. [6.1]

Note that it’s “Game controller” in the final version

[Bugfix] The same device type names used in the Bluetooth KCM, are now used in it’s applet.

Better device names? Yay!

Tokodon

[Feature] Added an alert badge for pages in the sidebar. Currently only used for follow requests, but plan on adding it on other pages soon. [24.05]

The new alert system being used

[Feature] Allow popping out the status composer on desktop. This allows you to compose toots while doing something else in the application, like browsing or searching for the correct hashtag. [24.05]

Now you can write your status in a separate window

[Bugfix] I implemented more key navigation fixes. This set of fixes are centered around interacting with media attachments. There’s still a long way to go before you can use Tokodon from a keyboard alone, but it’s getting there. [24.05]

[Bugfix] Ported from Qt5Compat.GraphicalEffects. I usually don’t mention boring refactors, this one is special. Volker found that this old GraphicalEffects module eats up 4 MB of storage on Android so this is a pretty big win! [24.05]

[Bugfix] Numerous UnifiedPush notification fixes. It’s not perfect yet, but much better than it was before. [24.02]

NeoChat

[Bugfix] Simple change, I made the tabs in the developer tools full-width. [24.05]

Perfectly balanced

[Bugfix] Now lonely question marks are excluded from links as they should be. Websites that don’t handle this will throw up an error instead, so this eliminates lots of user error. [24.02]

Now I can actually click these!

[Bugfix] Fixed the quick format bar not working. [24.02]

PlasmaTube

Lots of small UX changes this month. Including moving more actions to the header, reducing video title duplication and more. I can’t make a good screenshot right now because Invidious is currently broken due to Google’s changes. [24.05]

Kirigami

[Bugfix] I did a little digging on where one of the color roles came from, and now noted where the disabled text color comes from (on KDE Plasma). Still needs approval though 🙂 [6.1]

[Bugfix] (Not approved yet) Stop the back button from appearing even when we explicitly requested it gone. [6.1]

Documentation

Note that Plasma Framework is now libplasma in the Breeze README. [6.1]

Clarify that the default alignment for Kirigami’s ActionToolbar is AlignLeft, not AlignRight. [6.1]


That’s all for this month!

I'm bit late to the train of Plasma 6 related posts... But anyway. I will go through some things I did.

For me working on Plasma 6 was pretty fun, I learned a lot of new things and fixed bunch of bugs and crashes.

The most resourceful ones can find my merge requests, but I am too lazy to link them all.

Things I did

Most of the things I did were a lot more in the background. I hunted down a lot of bugs and crashes, and tried to fix them myself or helped others fix them, in various projects, such as:

  • Dolphin
  • Plasmashell
  • Kwin
  • KNS
  • And probably a lot of more I have already forgot... :D

But there was A LOT stuff: Around 100 merge requests in total! Pretty much all of them got in, thanks to all the reviews and education other KDE developers provided to me! :)

Again, thanks to everyone who has helped me to work on KDE projects! Thanks for your patience with me and all the knowledge you have parted to me. ❤️ I'll keep doing my best helping KDE projects, be it bug hunting or feature creation.

I have to say my software testing background has been very useful when it comes down to hunting down bugs, and I've learned a lot of things about Qt, C++ and QML. Still got much more to learn though, but that's what makes me so excited about programming!

I think one of the most useful things I've learned is how to use GDB. I can't provide anyone a crash course (at least in this blog post) but it is essential when hunting down weird bugs in plasmashell for example. I love debuggers integrated to editors like using LLDB-DAP in Kate, but sometimes GDB in terminal is all you can use, so it's good idea to learn to use it!

One big thing I worked on with others was fractional scaling related stuff: I didn't do any of the Kwin stuff around it, but I hunted down some weird bugs with window decorations having some weird gaps in them when windows are specific size. Hunting down all these bugs and weirdness took long time, and we're still looking into it, since it seems to be different in every system.. Floating point numbers and computers are very weird combo.

Another more visible thing I did was unifying separator colors and other items, you can find an issue about it here: Frame and outline color consistency and high-contrast setting changes. I have been bothered by the random differences between some elements, which can be especially noticeable in darker colorschemes, so I finally sat down and combed through related codebases. There's likely more to fix though, but there is now easy way for us to add high-contrast support for outlining elements! It just needs doing, and I haven't had the time.. Yet. :)

Things I learned

  • GDB is a life saver
  • Write down notes. All the time.
    • Journaling is a good idea!
  • Working in open source is a lot about the social aspects!
    • Be nice to people! Duh!
    • But also don't let people walk over you!
    • Listen to others, and don't be afraid to share your opinion.
    • Ask many questions and write down the answers.
  • Be patient
  • Bug triaging is tough, but very important!
  • Remember to rest (I'm bad at this)

Things I will do in future

I will continue hunting down various bugs and crashes and fixing them whenever I spot some, or something is raised to me as "hey this looks like something you could do."

I have also started working on couple things related to remote desktop:

Last but not least, I have looked into facelifting our dear Breeze theme, just a lil bit. Nothing drastic, some tell me they don't see any change and some do. But hopefully it would make Breeze look just a lil bit "softer" and "friendlier." :) You can see them here: Slightly rounder elements, slightly lighter outlines


All in all I am very happy with my current job working on KDE projects, fixing bugs and creating new cool things. I also kind of enjoy being a jack-of-all-trades (master of none), since I get to do a lot of different kinds of stuff, from something more "background" like KWin and plasmashell to something more visible like Breeze and Dolphin. Maybe eventually I will specialize around something, but for now I am bit all over the place which is fine by me lol.

I hope that some of my work has helped you as well. :)

I'll keep doing my best and learning more. (And hopefully write more blogposts.. lol.)

Thanks for reading!

Friday, 29 March 2024

shell

主函数

  • 先创建一个Shell类型的类
  • 然后屏蔽信号ctrl+c与ctrl+D
  • 接着打印提示符,接收命令的输入
  • 判断该次命令是否含有exit clear !等信息,如果有,执行相应操作
  • 通过调用解析函数解析本次输入的命令

屏蔽信号

  • ctrl + C

    1
    signal(SIGINT, SIG_IGN);//将该信号的处理方式设置为忽略
  • ctrl + D

    1
    2
    3
    4
    struct termios term;
    tcgetattr(STDIN_FILENO, &term);
    term.c_cc[VEOF] = _POSIX_VDISABLE;
    tcsetattr(STDIN_FILENO, TCSANOW, &term);

首先定义一个termios类型的结构体,用来存储终端相关的属性信息

然后通过调用tcgetattr函数将获取终端的相关属性,并将这些属性保存到 **term **结构体中,第一个参数表示标准输入,是一个预定的文件描述符常量

将结构体中的c_cc数组成员的VEOF设置为禁用. c_cc数组是包含终端特殊字符的数组,VEOF是其中的一个索引,第二个参数是一个特殊的宏,用于禁用该特殊字符

最后,tcsetattr(STDIN_FILENO, TCSANOW, &term);调用tcsetattr()函数,它用于设置终端的属性。STDIN_FILENO表示标准输入文件描述符,TCSANOW表示立即生效的选项,&term是要设置的终端属性结构体。

tcgetattr

1
int tcgetattr(int fd, struct termios *termios_p);

参数说明:

  • fd 是一个打开的终端设备文件描述符。

  • termios_p 是一个指向 termios 结构体的指针,用于存储获取

    到的终端属性。

tcgetattr 函数返回一个整数值,表示函数调用的成功与否。如果函数成功执行,返回值为0;如果出现错误,返回值为-1,并设置相应的错误码。在调用该函数时,你需要检查返回值以确保函数调用是否成功。

tcsetattr

1
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);

参数说明:

  • fd 是一个打开的终端设备文件描述符。
  • optional_actions 是一个表示操作选项的整数值,用于指定何时应用属性的更改。
  • termios_p 是一个指向 termios 结构体的指针,其中包含了要应用的终端属性。

optional_actions 参数用于指定何时应用属性的更改,它可以取以下三个值之一:

  • TCSANOW:立即更改属性。
  • TCSADRAIN:在所有输出都被传输后更改属性。
  • TCSAFLUSH:在所有输出都被传输后更改属性,并丢弃所有未读的输入。

print

getlogin

1
2
char* getlogin(void)
//返回一个指向当前登陆用户的用户名的字符串指针

strcspn

1
2
3
4
size_t strscpn(const char *str1,const char *str2);
//str1:要搜索的字符串
//str2:要查找的字符集合
//计算字符串中连续不包含指定字符集合的最大前缀长度。

strftime

1
size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr);

参数说明:

  • str 是一个指向字符数组的指针,用于存储格式化后的时间字符串。
  • maxsizestr 所指向的字符数组的最大容量。
  • format 是一个格式字符串,用于定义输出的时间格式。
  • timeptr 是一个指向 struct tm 结构体的指针,其中包含要格式化的时间和日期。
1
2
3
4
chrono::system_clock::now();
//是c++<chrono>头文件中的一个函数调用,用于获取当前的系统时间点
chrono::system_clock::to_time_t
//是一个函数模板,将time_point对象转换为time_t的类型

mycd

getenv

1
char* getenv(const char* name);

参数说明:

  • name是一个指向以null结尾的字符串,表示要获取的环境变量的名称.

根据指定的环境变量的名称,返回对应的环境变量的值。找到则返回一个null结尾的字符串,表示该环境变量的值.否则返回null.

1
2
const char* path = s[1].c_str();
//将string对象转换为c风格的字符串.

需要注意的是c_str()返回的指针指向的仍为string内部存储的字符数组,因此需要确保在使用path指针时,string对象仍然有效

get_current_dir_name

是一个标准的POSIX函数,用于获取当前工作目录的路径

由于get_current_dir_name( )分配了动态内存,因此使用完毕后需要调用free( )。

setenv

1
int setenv(const char* name, const char* value, int overwrite);

参数说明:

  • name 是一个指向以 null 结尾的字符串,表示要设置或新建的环境变量的名称。
  • value 是一个指向以 null 结尾的字符串,表示要设置的环境变量的值。
  • overwrite 是一个整数值,指示在环境变量已经存在时是否进行覆盖。如果 overwrite 为非零值,则会覆盖现有环境变量;如果 overwrite 为零,则不进行覆盖。

parse

istringstream是C++标准库中的一个类,他是用于从字符串中进行输入操作的输入流类.

getline()

1
std::istream& getline(std::istream& is, std::string& str, char delim);

参数说明:

  • is 是一个输入流对象,表示要从中读取文本的输入流,通常是 std::cin(标准输入)或文件流对象。
  • str 是一个字符串对象的引用,表示读取的文本将存储在其中。
  • delim 是一个字符(默认为换行符 \n),表示行的结束符。

pipe

“pipe”(管道)是一种在进程间进行通信的机制。它允许一个进程的输出直接成为另一个进程的输入,从而实现进程间的数据传输。

  1. 匿名管道:
    • 匿名管道是最常见的管道类型。它是一种单向通信管道,只能在具有父子关系的进程之间使用。父进程创建管道后,可以通过文件描述符进行读取或写入。子进程继承了父进程的文件描述符,从而可以使用相反的操作进行读取或写入。
    • 匿名管道是一种半双工通信方式,即数据只能在一个方向上流动。如果需要双向通信,需要创建两个匿名管道。
    • 使用匿名管道的典型流程是:父进程创建管道,然后调用 fork() 创建子进程。父进程关闭不需要的管道端口,子进程关闭另一个不需要的端口。然后父进程可以向管道写入数据,子进程可以从管道读取数据。
  2. 命名管道:
    • 命名管道也称为FIFO(First-In-First-Out),它提供了一种无关进程关系的进程间通信方式。命名管道在文件系统中有一个与之相关联的路径名,多个进程可以通过这个路径名来进行通信。
    • 命名管道是一种半双工通信方式,类似于匿名管道,只能在一个方向上流动数据。
    • 命名管道可以使用 mkfifo 函数创建,也可以使用命令行工具 mkfifo 创建。

管道的使用可以通过系统调用函数来实现,例如 POSIX 标准中的 pipe() 函数。在使用管道时,重要的是要了解管道的读取和写入端口以及数据的流动方向,以确保正确的通信。

1
int pipe(int pipefd[2]);

pipe() 函数接受一个整型数组 pipefd[2],用于存储管道的文件描述符。pipefd[0] 表示管道的读取端口,pipefd[1] 表示管道的写入端口。该函数返回值为 0 表示成功,-1 表示失败。

以下是 pipe() 函数的详细解释:

  • pipefd 参数是一个包含两个元素的整型数组,用于存储管道的文件描述符。pipefd[0] 是管道的读取端口,用于从管道中读取数据;pipefd[1] 是管道的写入端口,用于向管道中写入数据。
  • pipe() 函数创建一个匿名管道,并将管道的读取端口和写入端口的文件描述符填充到 pipefd 数组中。
  • 匿名管道是一种半双工通信方式,数据只能在一个方向上流动。如果需要双向通信,需要创建两个匿名管道。
  • 管道是内核中的一个缓冲区,用于在相关进程之间传输数据。数据写入管道后,可以从管道的读取端口读取。
  • 管道的大小是有限的,一旦管道的缓冲区满了,进程写入数据时可能会被阻塞,直到有足够的空间来容纳数据。同样,如果管道为空,从管道读取数据时可能会被阻塞,直到有数据可读。
  • 如果一个进程关闭了管道的读取端口,而另一个进程关闭了管道的写入端口,这两个进程之间的管道通信将结束。

dup2

1
2
3
#include <unistd.h>

int dup2(int oldfd, int newfd);
  • oldfd 参数是需要复制的文件描述符。它可以是任何有效的文件描述符,包括标准输入(0)、标准输出(1)和标准错误(2)。
  • newfd 参数是要复制到的新文件描述符。如果 newfd 已经打开,则会先关闭它,然后将 oldfd 复制到 newfd
  • 复制后,newfd 将和 oldfd 指向相同的文件表项,它们共享同一个文件偏移量和文件状态标志。
  • 如果 newfd 等于 oldfd,则 dup2() 函数不进行任何操作,直接返回 newfd
  • dup2() 函数的一个常见用途是重定向标准输入、标准输出和标准错误。例如,可以将标准输出重定向到一个文件,或者将标准错误重定向到一个套接字。

executeCommand

open

  • command[i + 1].c_str()command是一个字符串向量,command[i + 1]表示该向量中的第i + 1个元素。.c_str()将该元素转换为C风格的字符串,以便与open()函数的参数类型匹配。这个参数是要打开的文件的路径。
  • O_WRONLY:是open()函数的一个打开模式,表示以只写方式打开文件。这意味着你可以向文件写入数据,但不能读取文件的内容。
  • O_CREAT:是open()函数的一个打开模式,表示如果文件不存在,则创建该文件。如果文件已经存在,open()函数仍然会成功打开文件。
  • O_TRUNC:是open()函数的一个打开模式,表示如果文件已经存在且以只写方式打开,那么在打开文件时将截断文件的长度为0。这意味着打开文件后,文件中已经存在的内容将被清空。
  • 0644:表示文件的权限。在这里,0644表示文件所有者具有读和写的权限,而其他用户只有读的权限。它是一个八进制数,其中第一个数字0表示这是一个八进制数,后面的三个数字分别表示文件所有者、文件所属组和其他用户的权限。

代码

shepp.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
using namespace std;
#include <string>
#include <vector>
#include <sstream>
#include <chrono>
#include <unistd.h>
#include <csignal>
#include <algorithm>
#include <termios.h>
#include <cstdlib>
#include <wait.h>
#include <fstream>
#include <sys/stat.h>
#include <fcntl.h>

class Shell
{
public:
void print();
void setlinebuf();
string retlinebuf();
void setsignal();
bool isempty();
void addHistory(const string& line);
void setEOF();
void printHistory();
void searchAndReplay(const string &keyword);
void mycd(vector<string> &s);
void executeCommand(const vector<string> &command);
void parse(string &s);
private:
string linebuf;
vector<string> history;
};

func.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
#include "shell.hpp"

void Shell::print()
{ cout << "----------------------------------------------------------------------------" << endl;
cout << " ___ ___" << endl;
cout << " ______ / /___ ______ _____ ___ ___ ____ _____/ /___" << endl;
cout << " / __ \\/ __ \\ / ___ - ___ \\/ / / / /_ / / __/ __ \\" << endl;
cout << "/ /_/ / / / / / / / / / / /__/ / / /_(__ ) / / /" << endl;
cout << "\\______/__/ /__/ /__/ /__/ /__/\\____, / /____/____/__/ /__/" << endl;
cout << " /_______/" << endl;
cout << "----------------------------------------------------------------------------" << endl;
char cwd [1024];
auto now = chrono::system_clock::now();//返回当前的时间
time_t time = chrono::system_clock::to_time_t(now);//将time_t point转换为time_t类型
tm* timeinfo = std::localtime(&time);//将time_t转换为本地时间年月日,并存储在timeinfo中
char timeStr[9];
strftime(timeStr, sizeof(timeStr), "%H:%M:%S", timeinfo);
//std::strftime()将std::tm结构的时间信息格式化为字符串,并将结果存储在指定的字符数组中。这里使用"%H:%M:%S"作为格式字符串
//,它表示小时、分钟和秒的格式。timeStr是目标字符数组,sizeof(timeStr)用于指定数组的大小,
//timeinfo是包含时间信息的std::tm结构的指针。
cout << "# future @ future-arch in " << getcwd(cwd,1024) << " [" << timeStr << "]" << endl;
cout << "$ " ;
fflush(stdout);
}

void Shell ::setlinebuf()
{
getline(cin,this->linebuf);
}

string Shell::retlinebuf ()
{
return this->linebuf;
}

void Shell:: setsignal()
{
signal(SIGINT, SIG_IGN);//忽略ctrl+c的操作
}

bool Shell ::isempty()
{
if(this->linebuf.empty())
{
return true;
}
return false;
}

void Shell::addHistory(const string& line)
{
for(vector<string>::iterator it = history.begin(); it != history.end();it++)
{
if((*it).compare(line) == 0)
return ;
}
history.push_back(line);
}

void Shell ::setEOF()
{
struct termios term;//存储终端的相关信息
tcgetattr(STDIN_FILENO, &term);//获取相关信息,并存入结构体
term.c_cc[VEOF] = _POSIX_VDISABLE;//禁止使用EOF
tcsetattr(STDIN_FILENO, TCSANOW, &term);//将设置存入终端
}

void Shell::printHistory()
{
for(vector<string>::iterator it = this->history.begin(); it != this->history.end();it++)
{
cout << *it << endl;
}
cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~"<< endl;
}

void Shell::searchAndReplay(const string& keyword)
{
for (const string& line : history)
{
if (line.find(keyword) != string::npos)
{
cout << "Replaying: " << line << endl;
linebuf = line;
break;
}
}
}

void Shell::parse(string& s)
{
vector<vector<string>> tokens;
istringstream iss(s);//可方便的从字符串中读取数据
string root;
while (getline(iss, root, '|'))//将s以|分割开
{
istringstream root_iss(root);
vector<string> root_tokens;
string token;
while (root_iss >> token)//将单个命令以空格分隔开
{
root_tokens.push_back(token);
}

tokens.push_back(root_tokens);
}
if (tokens.size() == 1)
{
vector<string>& command = tokens[0];
if (command[0] == "cd")
{
mycd(command);
}
else
{
executeCommand(command);
}
}
else if (tokens.size() > 1)
{
int numPipes = tokens.size() - 1;
vector<int> pipefds(2 * numPipes, 0); // 用于存储管道的文件描述符
for (int i = 0; i < numPipes; ++i)
{
if (pipe(pipefds.data() + i * 2) < 0)
{
perror("pipe()");
exit(1);
}
}
int commandIndex = 0;
for (auto& command : tokens)
{
pid_t pid = fork();
if (pid < 0)
{
perror("fork()");
exit(1);
}
if (pid == 0)
{
// 子进程
if (commandIndex != 0) // 不是第一个命令,读取前一个管道的输出
{
if (dup2(pipefds[(commandIndex - 1) * 2], STDIN_FILENO) < 0)
{
perror("dup2()");
exit(1);
}
}
if (commandIndex != numPipes) // 不是最后一个命令,将输出写入下一个管道
{
if (dup2(pipefds[commandIndex * 2 + 1], STDOUT_FILENO) < 0)
{
perror("dup2()");
exit(1);
}
}
// 关闭所有管道的文件描述符
for (int i = 0; i < 2 * numPipes; ++i)
{
close(pipefds[i]);
}
if (command[0] == "cd")
{
mycd(command);
}
else
{
executeCommand(command);
}

exit(0);
}
else
{
// 父进程
++commandIndex;
}
}
// 关闭所有管道的文件描述符
for (int i = 0; i < 2 * numPipes; ++i)
{
close(pipefds[i]);
}

// 等待所有子进程结束
for (size_t i = 0; i < tokens.size(); ++i)
{
wait(NULL);
}
}
}

void Shell::mycd(vector<string>& s)
{
if (s.size() > 2)
{
cout << "cd: 函数调用参数过多" << endl;
return;
}
else if (s.size() == 1 || s[1].compare("~") == 0)
{
const char* homeDir = getenv("HOME");
if (homeDir)
{
if (chdir(homeDir) == -1)
{
perror("chdir()");
}
}
else
{
cout << "cd: 无法获取家目录路径" << endl;
return;
}
}
else if (s[1].compare("-") == 0)
{
const char* lastDir = getenv("OLDPWD");
if (lastDir)
{
if (chdir(lastDir) == -1)
{
perror("chdir()");
}
}
else
{
cout << "cd: 无法找到上一个路径" << endl;
}
}
else
{
const char* path = s[1].c_str();
if (chdir(path) == -1)
{
perror("chdir()");
}
}
// 更新环境变量 OLDPWD
char* currentDir = get_current_dir_name();//获取当前工作目录的路径
if (currentDir)
{
if (setenv("OLDPWD", currentDir, 1) == -1)//设置环境变量
{
perror("setenv()");
}
free(currentDir);
}
else
{
perror("get_current_dir_name()");
}
}

void Shell::executeCommand(const vector<string>& command)
{
pid_t pid;
fflush(NULL);
pid = fork();
if (pid < 0)
{
perror("fork()");
}
if (pid == 0)
{
vector<char*> args;
vector<int> outputFds; // 存储输出文件描述符的容器

for (size_t i = 0; i < command.size(); ++i)
{
if (command[i] == ">") // 输出重定向:覆盖写入文件
{
int fd = open(command[i + 1].c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1)
{
perror("open()");
exit(1);
}
outputFds.push_back(fd); // 将新的文件描述符添加到容器中
dup2(fd, STDOUT_FILENO);
close(fd);
i++;
}
else if (command[i] == ">>") // 输出重定向:追加写入文件
{
int fd = open(command[i + 1].c_str(), O_WRONLY | O_CREAT | O_APPEND, 0644);
if (fd == -1)
{
perror("open()");
exit(1);
}
outputFds.push_back(fd); // 将新的文件描述符添加到容器中
dup2(fd, STDOUT_FILENO);
close(fd);
i++;
}
else if (command[i] == "<") // 输入重定向
{
int fd = open(command[i + 1].c_str(), O_RDONLY);
if (fd == -1)
{
perror("open()");
exit(1);
}
dup2(fd, STDIN_FILENO);
close(fd);
i++;
}
else if (command[i] == "<<") // Here文档
{
string delimiter = command[i + 1];
string inputText;

while (getline(cin, inputText))
{
if (inputText == delimiter)
break;
inputText += "\n";
write(STDIN_FILENO, inputText.c_str(), inputText.size());
}

i++;
}
else
{
args.push_back(const_cast<char*>(command[i].c_str()));
}
}

args.push_back(nullptr); // execvp 需要以 nullptr 结尾的参数数组
execvp(args[0], args.data());

// execvp 执行失败时才会执行到这里
perror("execvp()");
exit(1);
}
else
{
wait(NULL);
}
}

shell.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include "func.cpp"

int main()
{
int num = 1;
Shell mysh;
mysh.setsignal();
mysh.setEOF();
while(num)
{
mysh.print();
mysh.setlinebuf();
string t = mysh.retlinebuf();
if(t.empty())
{
cout << "No input" << endl;
}
else if(t.rfind("exit")!=string::npos)
{
break;
}
else if(t.find("!")==0)
{
string keyword = t.substr(1); //获取去除感叹号的关键字
mysh.searchAndReplay(keyword);
}
else if(t.find("clear")==0)
{
mysh.addHistory(t);
// cout << "\033[2J\033[1;1H";//通过转义序列清除终端,但只能清楚当次的命令
system("clear");
num++;
}
else
{
mysh.addHistory(t);
num++;
}
mysh.parse(t);
if(num%3==0)
{
cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~"<< endl;
cout << " History Order :" << endl;
mysh.printHistory();
}
}
return 0;
}

后续问题

  1. 在输入命令全为空格的时候,会直接推出程序,造成程序崩溃.

    1. 是在parse中解析命令的时候,由于全为空格,所以会将一个空字符串token放入命令数组tokens,导致tokens.size()不为0造成exec函数的执行出现错误

    2. 所以在开始解析命令的时候加上全为空格的判断.

      1
      2
      3
      4
      if(std::all_of(s.begin(),s.end(),::isspace))
      {
      return {};
      }
    3. 在全为空格的时候直接返回,这样就可以返会空的二维数组.

  2. 缺乏对乱码命令的提示:例如输入”djsakldjalkjsduj”,等命令没有类似zsh : command not found : jdklasjdlak的提示,而是由于exec函数的无法执行出现的提示.

  3. 对于主题打印的提示,可以将Oh my zsh的打印只放在初始的时候,而不是每一次都打印.

  4. 另外在输出主机名称时,输出的是固定的作者的主机名称,这里为了偷懒,未使用gethostname()来获取主机名.

  5. 可以考虑对命令的存储进行改进,运用其他的结构存储,而不是二维数组.

  6. 增加类与类之间的联系,而不是简单的创造一个类,这样的话就只是相当于类似头文件的作用,并没有过多的用到一些c++的特性之类的东西.

Wednesday, 27 March 2024

KTextAddons 1.5.4 is a bugfix release of our text display and handling library

It fixes two notable bugs and updates translations

  • Fix bug 484328: Kmail config dialog takes about 40 seconds to show up
  • Use QListView::clicked (in emoji popup menu)

URL: https://download.kde.org/stable/ktextaddons/
Source: ktextaddons-1.5.4.tar.xz
SHA256: 64b80602e84b25e9164620af3f6341fa865b85e826ab8f5e02061ae24a277b20
Signed by: E0A3EB202F8E57528E13E72FD7574483BB57B18D Jonathan Esk-Riddell jr@jriddell.org https://jriddell.org/esk-riddell.gpg

Saturday, 23 March 2024

Tellico 3.5.4 is available, with a few fixes.

Improvements and Bug Fixes

  • Fixed bug with opening help from the config dialog (Bug 479591).
  • Updated Open Library source to search for comics (Bug 479506).
  • Fixed bug with filter dialog buttons (Bug 479771).
  • Fixed display bug with entry status (Bug 479843).
  • Fixed bug for entry selection after changing group field (Bug 480297).
  • Fixed DVDFr searches with title accents.
  • Updated FilmAffinity data source.

For a while Qt has been offering qmlls, a Language Server Protocol implementation for QML. This allows popular text editors like Kate (and some lesser known ones like Visual Studio Code) to work with QML code without having to write dedicated support for it.

Naturally many people are eager to use it to hack on KDE code. When trying to do that you probably have encountered some frustration with things not working as expected. So what’s going on there, and how can we make it work?

First and foremost one must mention that qmlls is still under heavy development. There’s a number of things missing that you’d expect from a fully featured LSP implementation. If you encounter something that looks like it should work but doesn’t, don’t hesitate to file a bugreport.

So how does one use qmlls? In Kate it should be activated out-of-the-box when opening a QML file. If that doesn’t work you might need to tweak the LSP-Client settings. For other editor please consult their documentation on how to enable LSP support.

One problem you are likely to encounter when using qmlls on KDE code is that it fails to find KDE-provided QML modules like Kirigami. This happens when the modules are installed into e.g. ~/kde/usr, which isn’t searched by Qt. One way to get around this is to build and install your own Qt into ~/kde/usr too, since that way the modules will be installed into the same prefix as Qt and Qt will find them. While building your own Qt is a worthwile thing to do in general it’s not a very satisfying solution. I hope we can find a better solution for this soon. See here for a related bugreport.

If your installation is set up in a way that qmlls can find the KDE-provided modules you might still encounter it warning on unknown modules or types. In order for qmlls to show information for a module the module needs a qmltypes file. These files provide machine-readable information about the types that the module exposes. The easiest way to make these available is porting the module to ecm_add_qml_module and declarative type registration. This was done for many KDE modules already, but there’s still a number of them missing. Help on that is very welcome! Something that isn’t obvious is that in order for the tooling to work correctly module dependencies need to be marked explicity via the DEPENDENCIES parameter in ecm_add_qml_module.

If all the modules and types are found correctly you will still encounter some warnings, but most of these should correspond to actual issues and non-optimal practices like using unqualified property lookups and context properties. qmlls is a great tool to guide you towards resolving those and thus modernizing and optimizing the code. There are however some classes of warnings for which we don’t have proper solutions yet:

  • Our translation functions, i18n and friends, are considered unqualified lookups by qmllint/qmlls. This is because they are magically injected into the engine at runtime. It’s not clear how a solution for this could look like.
  • When writing KCMs we expose the module’s C++ class via the kcm context property, which is opaque to the tools. A different approach is needed.

Despite the current limitations qmlls is already a very useful tool for working on QML code, assuming the project is set up properly.

Happy QML hacking!

If you are reading this from the future there’s an update available.

Wednesday, 20 March 2024

Recent events

A global theme on the kde third party store had an issue where it executed a script that removed user's data. It wasn't intended as malicious, but a mistake in some shell parsing. It was promptly identified and removed, but not before doing some damage to that user.

This has started a lot of discourse around the concept of the store, secuirty and upstream KDE. With the main question how can a theme have access to do this?

To developers it's not a surprise that third party plugins can do this sort of thing. It's as intended. A global theme can ship custom lockscreens, custom applets, custom settings and all of these can run arbitrary bundled code. You can't constrain this without limiting functionality.

To that end there's an explicit warning when downloading plugins.

Expectations

Our primary issue boils down to expectation management and communication.

There are plenty of other ways where users can download and run any other unfettered code from the internet; the Arch user repository (AUR), adding ubuntu PPAs and alike. This isn't a bad thing - it's useful and people do amazing things with this to benefit users.

Nothing on the KDE store happens without explicit user interaction either.

A problem is there's an expectation that because it's programs that it's inherently unsafe and a user needs to trust the source. Our issue is phrases like "global themes" or "Plasma applets" don't always carry this message.

The tech world has changed a lot over the past decade and whilst our code hasn't changed, users expectations have. More and more places provide well kept walled gardens where most actions accessible via the UI are safe-by-default - or at least claim to be!

I've also seen confusion that because a lot of our UI is written in a higher-level language (QML) that's enriched with javascript all browser sandboxing automatically applies. Even though that's not what we claim.

But ultimately if there is a gap in expectations, that's on us to fix.

What can we do better?

In the short term we need to communicate clearly what security expectations Plasma users should have for extensions they download into their desktops. Applets, scripts and services, being programs, are easily recognised as potential risks. It's harder to recognise that Plasma themes, wallpaper plugins and kwin scripts are not just passive artwork and data, but may potentially also include scripts that can have unintended or malicious consequences.

We need to improve the balance of accessing third party content that allows creators to share and have users to get this content easily, with enough speed-bumps and checks that everyone knows what risks are involved.


(UI from Flathub for potentially unsafe content)

Longer term we need to progress on two avenues. We need to make sure we separate the "safe" content, where it is just metadata and content, from the "unsafe" content with scriptable content.

Then we can look at providing curation and auditing as part of the store process in combination with slowly improving sandbox support.

Do I need to do anything as a user?

If you install content from the store, I would advise checking it locally or looking for reviews from trusted sources.

If you're a sys-admin you might want to consider adding the following to block users installing addons with the following kiosk snippet.

/etc/xdg/kdeglobals

[KDE Action Restrictions][$i]
ghns=false

For the next digiKam releases, the digiKam team needs photographs for digiKam and Showfoto splash-screens. Proposing photo samples as splash-screens is a simple way for users to contribute to digiKam project. The pictures must be correctly exposed/composed, and the subject must be chosen from a real photographer’s inspiration. Note that we will add a horizontal frame to the bottom of the image as in the current splashes.

Monday, 18 March 2024

Since the dawn of the times, the only way to implement any effect that has fancy user interface used to be in C++. It would be rather an understatement to say that the C++ API is difficult to use or get it right so there are no glitches. On the other hand, it would be really nice to be able to implement dashboard-like effects while not compromise on code simplicity and maintainability, especially given the rise of popularity of overview effects a few years ago, which indicated that there is demand for such effects.

In order solve that problem, we started looking for some options and the most obvious one was QtQuick. It’s a quite powerful framework and it’s already used extensively in Plasma. So, in Plasma 5.24, we introduced basic support for implementing kwin effects written in QML and even added a new overview effect. Unfortunately, if you wanted to implement a QtQuick-based effect yourself, you would still have to write a bit of C++ glue yourself. This is not great because effects that use C++ are a distribution nightmare. They can’t be just uploaded to the KDE Store and then installed by clicking “Get New Effects…”. Furthermore, libkwin is a fast moving target with lots of ABI and API incompatible changes in every release. That’s not good if you’re an effect developer because it means you will need to invest a bit of time after every plasma release to port the effects to the new APIs or at least rebuild the effects to resolve ABI incompatibilities.

This has been changed in Plasma 6.0.

In Plasma 6, we’ve had the opportunity to address that pesky problem of requiring some C++ code and also improve the declarative effect API after learning some lessons while working on the overview and other effects. So, enough of history and let’s jump to the good stuff, shall we? 🙂

Project Structure

Declarative effects require some particular project structure that we need to learn first before writing any code

└── package
├── contents
│   └── ui
│   └── main.qml
└── metadata.json

The package directory is a toplevel directory, it should contain two things: a metadata.json file and a contents directory. The metadata.json file contains information about the name of the effect, what API it uses, the author, etc.

{
"KPackageStructure": "KWin/Effect",
"KPlugin": {
"Authors": [
{
"Email": "user@example.com",
"Name": "Name"
}
],
"Category": "Appearance",
"Description": "Yo",
"EnabledByDefault": false,
"Id": "hello-world",
"License": "MIT",
"Name": "Hello World"
},
"X-KDE-Ordering": 60,
"X-Plasma-API": "declarativescript"
}

The contents directory contains the rest of QML code, config files, assets, etc. Keep in mind that ui/main.qml is a “magical” file, it acts as an entry point, every effect must have it.

In order to install the effect and make it visible in Desktop Effects settings, you will need to run the following command

kpackagetool6 --type KWin/Effect --install package/

This is quite a lot to memorize. That’s why kwin provides an example qtquick effect that you can grab, tweak some metadata and you’re good to go. You can find the example project at https://invent.kde.org/plasma/kwin/-/tree/master/examples/quick-effect?ref_type=heads. Note that the example project also contains a CMakeLists.txt file, which provides an alternative way to install the effect by the means of cmake, i.e. make install or cmake --install builddir.

Hello World

Let’s start with an effect that simply shows a hello world message on the screen:

import QtQuick
import org.kde.kwin

SceneEffect {
id: effect

delegate: Rectangle {
color: "blue"

Text {
anchors.centerIn: parent
color: "white"
text: "Hello world!"
}
}

ScreenEdgeHandler {
enabled: true
edge: ScreenEdgeHandler.TopEdge
onActivated: effect.visible = !effect.visible
}

ShortcutHandler {
name: "Toggle Hello World Effect"
text: "Toggle Hello World Effect"
sequence: "Meta+H"
onActivated: effect.visible = !effect.visible
}

PinchGestureHandler {
direction: PinchGestureHandler.Direction.Contracting
fingerCount: 3
onActivated: effect.visible = !effect.visible
}
}

import QtQuick is needed to use basic QtQuick components such as Rectangle. import org.kde.kwin imports kwin specific components.

The SceneEffect is a special type that every declarative effect must use. Its delegate property specifies the content for every screen. In this case, it’s a blue rectangle with a “Hello World!” label in the center.

The ShortcutHandler is a helper that’s used to register global shortcuts. ShortcutHandler.name is the key of the global shortcut, it’s going to be used to store the shortcut in the config and other similar purposes. ShortcutHandler.text is a human readable description of the global shortcut, it’s going to be visible in the Shortcuts settings.

The ScreenEdgeHandler allows to reserve a screen edge. When the pointer hits that screen edge, some code can be executed by listening to the activated signal.

The PinchGestureHandler and SwipeGestureHandler allow to execute some code when the user makes a pinch or a swipe gesture, respectively.

effect.visible = !effect.visible toggles the visibility of the effect. When effect.visible is true, the effect is active and visible on the screen; otherwise it’s hidden. You need to set effect.visible to true in order to show the effect.

If you press Meta+H or make a pinch gesture or move the pointer to the top screen edge, you’re going to see something like this

Note that there are no windows visible anymore, it is the responsibility of the effect to decide what should be displayed on the screen now.

Displaying Windows

Being able to display text is great, but it’s not useful. Usually, effects need to display some windows, so let’s display the active window

    delegate: Rectangle {
color: "blue"

WindowThumbnail {
anchors.centerIn: parent
client: Workspace.activeWindow
}
}

The change is quite simple. Instead of displaying a Text component, there’s a WindowThumbnail component now. The WindowThumbnail type is provided by the org.kde.kwin module. WindowThumbnail.client indicates what window the thumbnail item should display.

Input

Input processing contains no kwin specific APIs. TapHandler, MouseArea, Keys and other stuff available in QtQuick should just work. For example, let’s implement an effect that arranges windows in a grid and if somebody middle clicks a window, it will be closed

    delegate: Rectangle {
color: "pink"

GridView {
id: grid
anchors.fill: parent
cellWidth: 300
cellHeight: 300

model: WindowModel {}
delegate: WindowThumbnail {
client: model.window
width: grid.cellWidth
height: grid.cellHeight

TapHandler {
acceptedButtons: Qt.MiddleButton
onTapped: client.closeWindow()
}
}
}
}

The code looks pretty straightforward except maybe the model of the GridView. WindowModel is a helper provided by org.kde.kwin module that lists all the windows. It can be passed to various views, Repeater, and so on.

The result can be seen here

Delegates are Per Screen

One thing to keep in mind is that the delegates are instantiated per screen. For example,

    delegate: Rectangle {
color: "yellow"

Text {
anchors.centerIn: parent
color: "black"
text: SceneView.screen.name
}
}

When you activate the effect on a setup with several outputs, each output will be filled with yellow color and the output name in the center

Usually, the output is irrelevant, but if you need to know what output particular delegate is displayed on, you could use the SceneView.screen attached property.

Configuration

As your effect grows, you will probably face the need to provide an option or two. Let’s say that we want the background color in our hello world effect to be configurable. How do we achieve that? The first step, is to add a main.xml file in package/contents/config directory, i.e.

package/
├── contents
│   ├── config
│   │   └── main.xml
│   └── ui
│   └── main.qml
└── metadata.json

The main.xml file lists all options

<?xml version="1.0" encoding="UTF-8"?>
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
<kcfgfile name=""/>
<group name="">
<entry name="BackgroundColor" type="Color">
<default>#ff00ff</default>
</entry>
</group>
</kcfg>

In our case, only one option is needed: BackgroundColor, which has Color type and #ff00ff default value. You can refer to the KConfigXT documentation to learn more what other entry types are supported.

The next step is to actually use the BackgroundColor option

    delegate: Rectangle {
color: effect.configuration.BackgroundColor

Text {
anchors.centerIn: parent
color: "white"
text: "Hello world!"
}
}

effect.configuration is a map object that contains all the options listed in the main.xml.

Now, if you toggle the hello world effect, you’re going to see

There are a few more thing left to do though. If you navigate to Desktop Effects settings, you’re not going a configure button next to the hello world effect

Besides providing a main.xml file, the effect also needs to provide a config.ui file containing a configuration ui

package/
├── contents
│   ├── config
│   │   └── main.xml
│   └── ui
│   ├── config.ui
│   └── main.qml
└── metadata.json

The config.ui file is a regular Qt Designer UI file. The only special thing about it is that the controls that represent options should have special name format: kcfg_ + OptionName. For example, kcfg_BackgroundColor

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QuickEffectConfig</class>
<widget class="QWidget" name="QuickEffectConfig">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>455</width>
<height>177</height>
</rect>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Background color:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="KColorButton" name="kcfg_BackgroundColor">
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KColorButton</class>
<extends>QPushButton</extends>
<header>kcolorbutton.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

The last final piece in order to expose the configuration ui is to add the following line in the metadata.json file

"X-KDE-ConfigModule": "kcm_kwin4_genericscripted"

With all of that, the effect is finally displayed as configurable in the system settings and the background color can be changed

Sharing Your Effect With Other People

The preferred method to distribute third party extensions is via the KDE Store. Both JS and QML effects can be uploaded to the same “KWin Effects” category.

Documentation and Other Useful Resources

Documentation is still something that needs a lot of work (and writing it is no fun 🙁 ). KWin scripting API documentation can be found here https://develop.kde.org/docs/plasma/kwin/api/.

Besides the link above, it’s worth having a look at the examples https://invent.kde.org/plasma/kwin/-/tree/master/examples?ref_type=heads in kwin git repository.

Window Heap

If you need to pack or arrange windows like how the overview effect does, you could use the WindowHeap component from org.kde.kwin.private.effects module. BUT you need to keep in mind that that helper is private and has no stable API yet, so use it on your own risk (or copy paste the relevant code in kwin). Eventually, the WindowHeap will be stabilized once we are confident about its API.

The WindowHeap source code can be found at https://invent.kde.org/plasma/kwin/-/tree/2a13a330404c8d8a95f6264512aa06b0a560f55b/src/plugins/private.

More Examples

If you need more examples, I suggest to have a look at the desktop cube effect in kdeplasma-addons. It’s implemented using the same QML effect API in kwin + QtQuick3D. The source code can be found at https://invent.kde.org/plasma/kdeplasma-addons/-/tree/master/kwin/effects/cube?ref_type=heads

Conclusion

I hope that some people find this quick introduction to QML-based effects in KWin useful. Despite being a couple years old, declarative effects can be still considered being in the infancy stage and there’s a lot of untapped potential in them yet. The QML effect API is not perfect, and that’s why we are interested in feedback from third party extension developers. If you have some questions or feedback, feel free to reach out us at https://webchat.kde.org/#/room/#kwin:kde.org. Happy hacking!