Dopo qualche settimana di smanettamenti (e smadonnamenti!) vari sono riuscito ad ottenere qualche prototipo funzionante di desklet per cinnamon.
La prima cosa che mi viene da dire è che è veramente difficile riuscire a raccapezzarsi tra tutte le librerie usabili, quali sono le funzioni e come si usano!
Questo perchè il linguaggio usato per l'implementazione dei desklet è il JavaScript, mentre la documentazione delle librerie richiamabili è generata dal codice C, con la possibilità di essere richiamate da codice python o altri linguaggi. Come si immagina la situazione è veramente complessa.
La storia è che sono state inventate delle interfacce per chiamare queste librerie scritte in C dal JavaScript (o da altri linguaggi), raccolte sotto il nome di
GObject Introspection.
La documentazione di queste librerie segue lo standard C, per cui ad esempio la funzione
ClutterActor *gtk_clutter_embed_get_stage (GtkClutterEmbed *embed
);
va invocata in JavaScript come
const GtkClutter = imports.gi.GtkClutter;
this._clutterEmbed = new GtkClutter.Embed();
this._gtkEmbedActor = this._clutterEmbed.get_stage();
per cui la prima parte del nome della funzione gtk_clutter rappresenta la libreria da importare: GtkClutter; mentre l'oggetto GtkClutterEmbed diventa GtkClutter.Embed; e l'ultima parte del nome della funzione rappresenta come va chiamata la funzione (get_stage); tra l'altro in C non ci sono gli oggetti mentre in JavaScript la funzione viene invocata sull'istanza (this._clutterEmbed).
Documentazione
Un ottimo post che spiega una desklet di tipo
hello world è disponibile
qui.
Una fonte preziosa di informazioni è
quella ufficiale di linuxmint (i creatori di Cinnamon).
Un'altra ottima fonte di informazioni è il pacchetto devhelp, che contiene offline un sacco di reference utili; si installa con il comando
sudo apt install devhelp cinnamon-doc
Un esempio
Andando al sodo, ecco un codice di esempio che stampa un'immagine:
il file comune
metadata.json:
{
"uuid": "videoDesk@zac",
"name": "Video Desklet",
"description": "Show video streams on your desktop",
"icon": "",
"prevent-decorations": false,
"max-instances": "100",
"dangerous": false
}
il file
desklet.js:
const Desklet = imports.ui.desklet;
const St = imports.gi.St;
const Clutter = imports.gi.Clutter;
function VideoDesk(metadata, desklet_id)
{
this._init(metadata, desklet_id);
}
VideoDesk.prototype =
{
__proto__: Desklet.Desklet.prototype,
_init: function(metadata, desklet_id)
{
Desklet.Desklet.prototype._init.call(this, metadata, desklet_id);
this.window = new St.Bin();
let imgFilename = '/home/zac/Progetti/videoDesk/image.png';
this._clutterTexture = new Clutter.Texture({keep_aspect_ratio: true});
this._clutterTexture.set_from_file(imgFilename)
this._clutterBox = new Clutter.Box();
this._binLayout = new Clutter.BinLayout();
this._clutterBox.set_layout_manager(this._binLayout);
this._clutterBox.set_width(this.metadata["width"]);
this._clutterBox.add_actor(this._clutterTexture);
this.window.add_actor(this._clutterBox);
this.setContent(this.window);
}
}
function main(metadata, desklet_id)
{
return new VideoDesk(metadata, desklet_id);
}
Un trucco pratico
Durante l'implementaizone lancio spesso il desklet per verificare che la sintassi dei comandi che sto scrivendo sia corretta. Un'opzione è quella di aggiungere e rimuovere in continuazione il nostro desklet dal desktop. Ma ho trovato molto più comodo e veloce usare il compilatore di JavaScript di Cinnamon (il cjs). Tra l'altro ho impostato l'editor
Scite a lanciarlo ogni volta che premo F5 per cui l'esecuzione e visualizzazione degli errori è ancora più veloce.
In scite la configurazione per la compilazione automatica si ottiene aggiungendo al file di configurazione la linea
command.go.*.js=cjs $(FileNameExt)
il codice del file di test (che mostra un filmato):
#!/usr/bin/cjs
const Lang = imports.lang;
const Gtk = imports.gi.Gtk;
const Gst = imports.gi.Gst;
const Clutter = imports.gi.Clutter;
const ClutterGst = imports.gi.ClutterGst;
const GtkClutter = imports.gi.GtkClutter;
ClutterGst.init(null, null);
const Application = new Lang.Class({
//A Class requires an explicit Name parameter. This is the Class Name.
Name: 'Application',
//create the application
_init: function() {
this.application = new Gtk.Application();
//connect to 'activate' and 'startup' signals to handlers.
this.application.connect('activate', Lang.bind(this, this._onActivate));
this.application.connect('startup', Lang.bind(this, this._onStartup));
},
//create the UI
_buildUI: function() {
this._window = new Gtk.ApplicationWindow({ application: this.application, title: "Hello World!" });
this._window.set_default_size(200, 200);
this.label = new Gtk.Label({ label: "Hello World" });
this.player = new ClutterGst.Playback();
this.player.set_filename("/home/zac/Scrivania/test.mov");
this._content = new ClutterGst.Aspectratio();
this._content.set_player(this.player);
this._clutterBox = new Clutter.Box();
this._binLayout = new Clutter.BinLayout();
this._clutterBox.set_layout_manager(this._binLayout);
this._clutterBox.set_width(300);
this._clutterBox.set_height(100);
this._clutterBox.set_content(this._content);
this._clutterEmbed = new GtkClutter.Embed();
this._gtkEmbedActor = this._clutterEmbed.get_stage();
this._gtkEmbedActor.add_actor(this._clutterBox);
this._window.add(this._clutterEmbed);
this.player.set_playing(true);
},
//handler for 'activate' signal
_onActivate: function() {
//show the window and all child widgetsq
this._window.show_all();
},
//handler for 'startup' signal
_onStartup: function() {
this._buildUI();
},
});
//run the application
let app = new Application();
app.application.run(ARGV);
Il prossimo passo è aprire un repository su
GitHub dove piazzare il codice dei miei esperimenti, nel frattempo, buona programmazione!