3.2 I plugin scriptabili
3.2.2 L’esempio plugins2
L’esempio succesivo tratta la seconda parte dei plugin scriptabili: rendere accessibile all’esterno del plugin funzioni e variabili interne. Per far ci`o `e necessario creare un
Listing 3.12: Struttura dati per plugins2. 1 s t r u c t _ m y _ o b j { 2 N P O b j e c t me ; /* the a c t u a l o b j e c t . */ 3 s t r u c t _ i n s t a n c e * i n s t a n c e ; /* P o i n t e r to te a c t u a l p l u g i n i n s t a n c e */ 4 N P I d e n t i f i e r f u n c 1 ; /* [ m y _ f n 1 ] The f u n c t i o n t h a t w i l l be a c c e s s i b l e f r o m j a v a s c r i p t */ 5 N P I d e n t i f i e r v a l I d ; /* [ v a l u e ] N P V a r i a n t r e p r e s e n t i n g the i n t e g e r val */ 6 }; 7 s t r u c t _ i n s t a n c e { 8 l o n g m W i n d o w ; /* W i n d o w id */ 9 int mX , mY ; /* L e f t h i g h c o r n e r c o o r d i n a t e s of the w i n d o w */ 10 int mWidth , m H e i g h t ; /* W i n d o w d i m e n s i o n */ 11 D i s p l a y * m D i s p l a y ; 12 s t r u c t _ m y _ o b j * o b j S ; /* A p o i n t e r to the s c r i p t a b l e o b j e c t */ 13 int val ; /* The v a l u e set and r e a d t h r o u g h j a v a s c r i p t */ 14 };
oggetto scriptabile (NPObject) e renderlo disponibile al browser. Questo esempio esporta una funzione e una variabile intera. Premendo il pulsante Run viene eseguita la funzione che mostra un messaggio, mentre con i pulsanti Set value e Get value si setta e si legge un valore intero.
Struttura dati usata
La struttura dati usata `e mostrata nel codice3.12. La struttura instance `e stata am-
pliata con un intero val che rappresenta la variabile esportata dal plugin, e con un puntatore a una struttura my obj che rappresenta l’oggetto scriptabile esportato al browser. All’oggetto standard NPObject `e stato sovrapposto un oggetto personaliz- zato my obj in modo tale da poter memorizzare ulteriori informazioni nell’oggetto scriptabile senza dover utilizzare variabili globali. All’interno di questo oggetto `e pre- sente un puntatore all’istanza del plugin in modo tale da poter accedere a tutti i dati del plugin, e i due identificatori7della funzione e della variabile esportati.
Inizializzazione
Le funzioni NPP New() e NPP SetWindow() sono simili all’esempio base. Nella prima viene allocato lo spazio necessario per memorizzare l’istanza e vengono inizializzati i vari membri, mentre la seconda stampa soltanto messaggi di informazione e di debug su terminale.
La funzione NPP GetValue() (codice 3.13) `e stata ampliata in modo tale da in-
formare il browser che il plugin crea ed esporta un oggetto scriptabile. Il caso
Listing 3.13: Parte della funzione NPP GetValue() per invocare la creazione dell’oggetto scriptabile, per plugins2.
1 [ . . . ] 2 c a s e N P P V p l u g i n S c r i p t a b l e N P O b j e c t : 3 d e b M e s (" % s : r e q u e s t e d % d ( N P P V p l u g i n S c r i p t a b l e N P O b j e c t ) \ n " , 4 _ _ F U N C T I O N _ _ , v a r i a b l e ) ; 5 *( N P O b j e c t **) v a l u e = m y _ G e t S c r i p t a b l e O b j e c t ( i n s t a n c e ) ; 6 b r e a k ; 7 [ . . . ]
Listing 3.14: La funzione my GetScriptableObject() per la creazione dell’ogget- to scriptabile, per plugins2.
1 N P O b j e c t * m y _ G e t S c r i p t a b l e O b j e c t ( NPP i n s t a n c e ) { 2 [ . . . ] 3 if (! m y _ i n s t a n c e - > o b j S ) { 4 d e b M e s (" % s : c r e a t i n g new o b j e c t \ n " , _ _ F U N C T I O N _ _ ) ; 5 m y _ i n s t a n c e - > o b j S = ( s t r u c t _ m y _ o b j *) 6 N P N _ C r e a t e O b j e c t ( i n s t a n c e , & x y z _ s c r i p t a b l e ) ; 7 if ( m y _ i n s t a n c e - > o b j S ) { 8 m y _ i n s t a n c e - > objS - > f u n c 1 = 9 N P N _ G e t S t r i n g I d e n t i f i e r (" m y _ f n 1 ") ; 10 m y _ i n s t a n c e - > objS - > v a l I d = 11 N P N _ G e t S t r i n g I d e n t i f i e r (" v a l u e ") ; 12 } 13 e l s e 14 d e b M e s (" % s : E r r o r c r e a t i n g new o b j e c t \ n " , _ _ F U N C T I O N _ _ ) ; 15 } e l s e { 16 d e b M e s (" % s : o b j e c t a l r e a d y e x i s t i n g \ n " , _ _ F U N C T I O N _ _ ) ; 17 N P N _ R e t a i n O b j e c t (( N P O b j e c t *) m y _ i n s t a n c e - > o b j S ) ; 18 } 19 r e t u r n ( N P O b j e c t *) ( m y _ i n s t a n c e - > o b j S ) ; 20 }
NPPVpluginScriptableNPObject rappresenta l’uso della scriptabilit`a attraverso l’u- so di oggetti NPObject. Quando il browser fa questa richiesta, viene chiamata la funzione my GetScriptableObject() che crea l’oggetto, e lo ritorna nella variabile value che viene passata al browser.
L’oggetto scriptabile
La funzione che effettua la creazione dell’oggetto scriptabile `e
my GetScriptableObject(). Questa funzione controlla se l’oggetto `e gi`a sta-
to creato in precedenza (codice 3.14 linea 3) e in caso affermativo8 incrementa il
8Il browser pu`o invocare la funzione NPP GetValue() pi`u volte durante l’esecuzione. In questi casi
contatore degli oggetti referenziati attraverso la funzione NPN RetainObject(linea
17). Con la chiamata alla funzione NPN CreateObject() viene effettuata la creazione
dell’oggetto scriptabile. Questa funzione ha come parametri l’istanza del plugin, e una struttura NPClass che rappresenta l’implementazione dell’oggetto. Successivamente
(linea9) viene associata all’identificatore my fn1 la chiamata alla funzione func1()
e all’identificatore value la variabile valId. Questi saranno i due identificatori per accedere alla funzione e alla variabile dall’esterno del plugin.
L’implementazione della NPClass
Poich`e abbiamo usato un oggetto personalizzato, `e necessario implementare le funzio- ni allocate() e deallocate() per allocare e deallocare lo spazio necessario per memorizzare l’oggetto. Nell’allocazione `e anche necessario inizializzare il puntatore all’istanza del plugin, in quanto l’istanza non sar`a accessibile dalle altre funzioni della classe. Le seguenti funzioni della NPCLASS sono state personalizzate in modo da informare il browser delle funzioni e delle variabili esportate:
la funzione hasProperty() dice al browser se un identificatore `e un membro dell’oggetto, in questo caso esiste solo la variabile valId;
la funzione getProperty() ritorna nel parametro Result il valore della variabile intera convertito nel tipo NPVariant;
la funzione setProperty() setta la variabile intera con il valore (convertito prima in intero) del parametro value;
la funzione hasMethod() informa il browser se l’indentificatore name `e una funzione dell’oggetto;
la funzione invoke() invoca la funzione name con gli eventuali parametri (in que- sto caso nessun parametro `e passato alla funzione); il risultato della funzione `e passato al browser attraverso il parametro result.
In tutte queste funzioni di accesso ai metodi e ai membri dell’oggetto `e sempre per prima cosa verificato il nome del membro o metodo da eseguire; se questo nome non combacia con quelli implementati nel plugin viene ritornato il valore false.
Le altre funzioni della NPClass sono state implementate come stub in quanto non necessarie per questo plugin.
Listing 3.15: L’implementazione della NPClass per l’oggetto scriptabile per plugins2. 1 s t a t i c N P O b j e c t * _ a l l o c a t e ( NPP i n s t a n c e , N P C l a s s * cl ) { 2 s t r u c t _ m y _ o b j * o = m a l l o c ( s i z e o f ( s t r u c t _ m y _ o b j ) ) ; 3 o - > i n s t a n c e = ( s t r u c t _ i n s t a n c e *) i n s t a n c e - > p d a t a ; 4 r e t u r n ( N P O b j e c t *) o ; 5 } 6 7 s t a t i c v o i d _ d e a l l o c a t e ( N P O b j e c t * o ) { 8 s t r u c t _ m y _ o b j * obj = ( s t r u c t _ m y _ o b j *) o ; 9 obj - > i n s t a n c e - > o b j S = N U L L ; 10 f r e e ( o ) ; 11 } 12 13 s t a t i c b o o l _ h a s P r o p e r t y ( N P O b j e c t * o , N P I d e n t i f i e r n a m e ) { 14 s t r u c t _ m y _ o b j * x = ( s t r u c t _ m y _ o b j *) o ; 15 r e t u r n ( n a m e == x - > v a l I d ) ; 16 } 17 18 s t a t i c b o o l _ g e t P r o p e r t y ( N P O b j e c t * o , N P I d e n t i f i e r name , N P V a r i a n t * r e s u l t ) { 19 s t r u c t _ m y _ o b j * x = ( s t r u c t _ m y _ o b j *) o ; 20 if ( n a m e == x - > v a l I d ) { 21 I N T 3 2 _ T O _ N P V A R I A N T ( x - > i n s t a n c e - > val , * r e s u l t ) ; 22 r e t u r n t r u e ; 23 } 24 r e t u r n f a l s e ; 25 } 26 27 s t a t i c b o o l _ s e t P r o p e r t y ( N P O b j e c t * o , N P I d e n t i f i e r name , c o n s t N P V a r i a n t * v a l u e ) { 28 s t r u c t _ m y _ o b j * x = ( s t r u c t _ m y _ o b j *) o ; 29 if ( n a m e == x - > v a l I d ) { 30 x - > i n s t a n c e - > val = N P V A R I A N T _ T O _ I N T 3 2 (* v a l u e ) ; 31 r e t u r n t r u e ; 32 } 33 r e t u r n f a l s e ; 34 } 35 36 s t a t i c b o o l _ h a s M e t h o d ( N P O b j e c t * o , N P I d e n t i f i e r n a m e ) { 37 s t r u c t _ m y _ o b j * x = ( s t r u c t _ m y _ o b j *) o ; 38 r e t u r n ( n a m e == x - > f u n c 1 ) ; 39 } 40 41 s t a t i c b o o l _ i n v o k e ( N P O b j e c t * o , N P I d e n t i f i e r name , 42 c o n s t N P V a r i a n t * argv , u i n t 3 2 _ t argc , N P V a r i a n t * r e s u l t ) { 43 s t r u c t _ m y _ o b j * x = ( s t r u c t _ m y _ o b j *) o ; 44 if ( n a m e == x - > f u n c 1 ) { 45 S T R I N G Z _ T O _ N P V A R I A N T ( s t r d u p (" T h i s s t r i n g is c r e a t e d by p l u g i n !") , 46 * r e s u l t ) ; 47 r e t u r n T R U E ; 48 } 49 r e t u r n F A L S E ; 50 }
Listing 3.16: Il codice HTML per l’esempio plugins2. 1 [ . . . ] 2 < e m b e d t y p e =" a p p l i c a t i o n / x - s2 " id =" t e s t " > </ embed > 3 < br / > < br / > 4 < form > 5 < i n p u t t y p e =" b u t t o n " v a l u e =" Run " o n c l i c k = a l e r t ( t e s t . m y _ f n 1 () ) > < br > 6 < i n p u t t y p e =" b u t t o n " v a l u e =" Set v a l u e " o n c l i c k = s e t V a l u e () > < br > 7 < i n p u t t y p e =" b u t t o n " v a l u e =" Get v a l u e " o n c l i c k = g e t V a l u e () > < br > 8 </ form > 9 < script > 10 f u n c t i o n s e t V a l u e () { 11 var res = w i n d o w . p r o m p t (" I n s e r t an i n t e g e r to p a s s to p l u g i n ") ; 12 var i = p a r s e I n t ( res ) ; 13 t e s t . v a l u e = i ; 14 } 15 f u n c t i o n g e t V a l u e () { 16 var res = t e s t . v a l u e ;
17 w i n d o w . a l e r t (" The i n t e g e r f r o m the p l u g i n is : " + res ) ; 18 }
19 </ script > 20 [ . . . ]
Il codice HTML per l’esempio
Il codice HTML per visualizzare l’esempio `e mostrato nel listato3.16. Si pu`o notare che
il plugin `e invocato attraverso il tag embed, al quale `e stato assegnato un id uguale a test, con il quale `e possibile accedere alle funzione e ai membri del plugin. Premendo il pulsante Run viene eeguita la funzione test.my fn1() e visualizzato il risultato attraverso una finestra Javascript di alert. Premendo i pulsanti Set value o Get value viene settata o visualizzato un intero, accedendo in scrittura o lettura al membro value del plugin.
Le applicazioni SDL: esempi di
integrazione in un plugin
SDL1 `e una libreria multipiattaforma scritta in C rilasciata sotto la licenza GNU LG-
PL2 per lo sviluppo di applicazioni multimediali. SDL crea un livello astratto al di
sopra di varie piattaforme software grafiche e sonore e dunque pu`o controllare video, audio, CDROM e altre periferiche. Essendo multipiattaforma, `e possibile creare appli- cazioni su un sistema operativo e portarlo in altri senza apportare modifiche importanti al codice. Le piattaforme supportare sono molte, a partire da Microsoft Windows, Linux/Unix, fino ad arrivare ai sistemi Windows CE e SymbianOS.
4.1
Breve introduzione a SDL
L’implementazione di SDL `e relativamente semplice: `e un wrapper leggero e multi piattaforma che fornisce il supporto alle operazioni 2D sui pixel, suoni, accesso ai file,
gestione degli eventi, temporizzatori, thread e altro. ´E spesso usata come complemento
alle OpenGL settando l’output grafico e fornendo la gestione dell’input del mouse e della tastiera, che sono ben oltre lo scopo delle OpenGL.
La libreria `e suddivisa in parecchi sottosistemi, tra i quali il Video (gestisce sia le funzioni per le superfici e l’OpenGL), l’Audio, il CD-ROM, il Joystick e il sottosiste- ma Timer. A parte il supporto basico a basso livello, vi sono alcune librerie ufficiali di supporto che forniscono funzionalit`a addizionali. Queste comprendono le librerie
standard, sono fornite sul sito ufficiale e incluse nella documentazione ufficiale3:
SDL image : supporto per diversi formati di immagini;
1http://www.libsdl.org/
2http://www.gnu.org/licenses/lgpl.html 3http://www.libsdl.org/cgi/docwiki.cgi
Figura 4.1: Livello di astrazione della libreria SDL per le piattaforme pi`u comuni.
SDL mixer : funzioni audio complesse, principalmente per il mixing dei suoni; SDL net : supporto alla rete;
SDL ttf : supporto alla renderizzazione dei font TrueType; SDL rtf : renderizzazione semplice del Rich Text Format.
Come si nota dalla figura4.1 su Microsoft Windows SDL fornisce un wrapper per le
DirectX4, mentre sulle piattaforme X11 SDL usa le Xlib per comunicare con il sistema
grafico sottostante5.
In questo capitolo vedremo attraverso alcuni esempi come `e possibile inglobare un’applicazione SDL in un plugin per Firefox. Il problema risiede nel fatto che di default un’applicazione SDL crea una nuova finestra per l’esecuzione. Esiste la possi- bilit`a di settare la variabile d’ambiente SDL WINDOWID con il valore di un windowid in modo da forzare l’esecuzione dell’applicazione in una finestra gi`a esistente. In questo modo per`o l’applicazione SDL non sar`a in grado di gestire nessun evento del mouse o della tastiera, n´e di ridimensionare la finestra.
Nei paragrafi successivi vedremo come sar`a possibile risolvere questo problema, sia effettuando modifiche all’applicazione che modificando la libreria SDL. La tratta- zione riguarder`a i sistemi operativi UNIX like; per una soluzione per i sistemi operativi
Microsoft Windows si vedi il capitolo6.
4a partire dalla versione 1.2 utilizza le DirectX 7. 5oppure il framebuffer se non `e usato un server X.
4.1.1
Le finestre di SDL
Vediamo brevemente l’architetture delle finestre usate da un applicazione SDL, quando viene eseguita nella propria finestra. Esistono 3 finestre create da SDL:
• WMwindow: `e la finestra che viene gestita dal window manager del sistema. ´E creata come finestra figlia della finestra root e non `e utilizzata per effettuare il disegno dell’applicazione, ma come contenitore della finestra SDL Window e per gestire alcuni eventi (come la gestione della tastiera, . . . ).6
• SDL Window: `e la finestra dove avviene il disegno dell’applicazione SDL. Questa finestra `e creata come figlia della finestra WMwindow e gestisce gli eventi del
mouse (pressione dei bottoni, movimento, . . . )7
• FSwindow: `e la finestra che si occupa di disegnare lo schermo quando si passa alla visualizzazione a schermo intero.
Quando si tenta di eseguire un’applicazione SDL all’interno di una fine-
stra esistente8, le due finestre SDL Window e WMwindow coincidono, e la finestra
FSwindow non viene creata. Inoltre non vengono effettuate le chiamate alla funzione XSelectInput() cosicch`e tutti gli eventi non possono essere gestiti dall’applicazione. Vedremo nel seguito del capitolo una possibile soluzione a questo problema.
4.2
L’applicazione di esempio
L’applicazione SDL che useremo come esempio9 `e un semplice programma che di-
segna un rettangolo riempito con un gradiente dal nero a uno dei tre colori primari, dando all’utente la possibilit`a di spostarsi da un gradiente all’altro premendo le frecce su/giu e di ingrandire o ridurre il rettangolo premendo le frecce destra/sinistra. Inol- tre `e aggiunta la possibilit`a di passare alla visualizzazione a schermo intero e torna- re alla modalit`a finestra premento semplicemente la barra spaziatrice. Per uscire dal programma `e sufficiente premere Esc o fare click sulla x della finestra.
6La maschera degli eventi associata a questa finestra `e: FocusChangeMask | KeyPressMask |
KeyReleaseMask | PropertyChangeMask | StructureNotifyMask | KeymapStateMask.
7in particolare la maschera degli eventi perquesta finestra sono: EnterWindowMask
LeaveWindowMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask.
8settando la variabile d’ambiente SDL WINDOWID con il valore di un window id valido 9si veda il file sdlEx/sdlVanilla.c
4.3
Lo sviluppo
Vedremo in questo paragrafo come `e stato risolto il problema dell’embedding dell’applicazione SDL di esempio all’interno di un plugin.
4.3.1
Il problema
Per mostrare il problema useremo l’applicazione sdlVanilla descritta in precedenza e un plugin (plugin2) che agisce da contenitore per l’applicazione.
Il plugin
Il plugin utilizzato per questo primo esempio `e sdlEx/plugin2. Il plugin crea una finestra vuota nella quale viene eseguita l’applicazione il cui percorso `e indicato nel- l’attributo progtorun passato dal codice HTML. Nella funzione NPP New() viene con- trollato se esiste l’attributo progtorun che conterr`a l’applicazione SDL da esegui- re all’interno del plugin. Durante la creazione della finestra vuota, nella funzione NPP SetWindow(), viene estratto il window id ed effettuato l’hack di SDL (cio`e viene settata la variabile d’ambiente SDL WINDOWID con il window id della finestra esistente), dopodich`e viene effettuata una fork ed eseguita l’applicazione SDL.
Esecuzione dell’applicazione sdlVanilla
Se proviamo ad eseguire l’applicazione sdlVanilla all’interno del plugin, invocando
il codice HTML mostrato nel listato 4.1, si nota che il rettangolo viene visualizzato
all’interno della finestra assegnata al plugin.
Listing 4.1: Codice HTML per eseguire l’applicazione sdlVanilla all’interno del plugin2.
< e m b e d t y p e =" a p p l i c a t i o n / x - p2 " p r o g t o r u n = " . / s d l V a n i l l a " > </ embed >
La pressione dei tasti o del mouse non ha nessun effetto, e non `e possibile cambiare colore allo sfondo ne ridimensionare la finestra. Inoltre si pu`o notare che sovrappo- nendo una qualsiasi finestra al rettangolo, questo non viene successivamente pi`u ridi-
segnato.10 Questo significa che l’applicazione SDL non riesce a gestire nessun tipo di
evento generico.