# Aiguillage sur la route à prendre pour simuler la souris



## Joan (9 Mai 2006)

Bonjour.

Voici mon petit problème : je cherche à créer un programme qui émule en quelque sorte un clic de souris. Par exemple je charge un swf dans une fenêtre de safari. On peut voir une balle qui roule d'un bord à l'autre et un bouton qui permet de rembobiner l'animation. 

Le programme en question permettrait par exemple de simuler un clic de souris sur à un endroit "x,y" de l'écran spécifié (le bouton par exemple) à intervalle régulier. 

Peut on réaliser cela en applescript ? sinon vers quel type de programmation dois je me tourner ?

Merci d'avance pour vos conseilles


----------



## Didier Guillion (9 Mai 2006)

A mon avis cela va etre tres difficile. Il n'est meme plus possible de "capturer" la forme du curseur sous Mac OS X (alors que c'était facile en Mac OS 9).
Mais, un avis plus pertinent est bienvenu.

Cordialement


----------



## Joan (9 Mai 2006)

j'ai trouvé une piste avec applescript : "keystroke".
Mais je n'arrive pas à trouver l'équivalent souris de ctte petite merveille.


----------



## Anonyme (9 Mai 2006)

Joan a dit:
			
		

> j'ai trouvé une piste avec applescript : "keystroke".
> Mais je n'arrive pas à trouver l'équivalent souris de ctte petite merveille.


Si j'ai bien compris, il ne s'agit pas de reproduire l'apparence du pointeur, mais d'automatiser un clic de souris sur un point précis de l'écran et pendant une durée déterminée (ou non). Si c'est bien ce que tu souhaites faire, je te suggère de te pencher sur les commandes de scriptage de l'interface détaillées dans la "Processes Suite" du dictionnaire de l'application "System Events".

Par exemple, le code suivant...

```
activate application "Finder"
tell application "System Events"
	tell application process "Finder"
		click group 1 of scroll area 1 at {100, 100}
	end tell
end tell
```
... simule un clic sur le Finder à la position 100 px, 100 px.


----------



## Joan (9 Mai 2006)

Merci enormement c'est exactement cela que je recherche : un clic de souris à un endroit de l'écran sauf qu'aucun utilisateur appuie sur le bouton de la souris.


----------



## Joan (9 Mai 2006)

J'ai réussi cela



> tell application "iCal"
> activate
> end tell
> tell application "System Events"
> ...



Ca marche super bien. Donc ce que je fais c'est un imprime ecran que j'ouvre avec graphicconverter ce qui me donne la position en pixel. En placant le bouton rouge pour fermer la fenêtre d'iCal ca marche super.

Je fais la même chose, sauf que je remplace iCal par Safari. Pour un lien ca marche super. Par contre si i y a un fichier flash dans la page il ne veut pas cliquer dans l'animation flash si le point du click se trouve dnas celle-ci : comment faire ?

Merci de votre patience avec le grand débutant que je suis.


----------



## Anonyme (10 Mai 2006)

Joan a dit:
			
		

> Je fais la même chose, sauf que je remplace iCal par Safari. Pour un lien ca marche super. Par contre si i y a un fichier flash dans la page il ne veut pas cliquer dans l'animation flash si le point du click se trouve dnas celle-ci : comment faire ?


J'ai fait quelques essais afin de te venir en aide, mais tous ont échoué. À vrai dire, à moins que quelqu'un propose une solution, cela ne me semble tout bonnement pas possible. Avec AppleScript du moins...
Si j'en ai le temps, je vais essayer de pousser mes investigations, mais je ne suis pas très optimiste.


----------



## Joan (10 Mai 2006)

Merci beaucoup d'avance pour tes recherches, même si elles ne sont pas fructueuses. J'en fais de même de mon côté.


----------



## Joan (10 Mai 2006)

Voila ce que l'on m'a répondu dans un autre forum :



> Dans ce cas, il est probable que Flash ne passe pas par la gestion d'évènements du système, et fonctionne à plus bas niveau que Carbon pour récupérer les évènements. Je pense qu'AppleScript passe sûrement par des fonctions Carbon, donc passer directement par Carbon ne va peut-être rien faire de plus, mais ça n'empêche pas d'essayer pour être sûr.


----------



## Céroce (11 Mai 2006)

A un niveau plus bas que Carbon? Il va lire les registres du contrôleur USB de la souris ou quoi? 

Non, c'est très improbable.

Cocoa gère de façon transparente les AppleEvents (événements passés par AppleScript, entre autres), ce n'est pas le cas des applis Carbon, où il est nécessaire de programmer soi-même cette gestion des AppleEvents. Tu sais, c'est la distinction entre les applis qui sont scriptables et celles qui ne le sont pas...

Le plug-in Flash n'est très certainement pas scriptable, il faut justement utiliser des fonctions Carbon pour simuler la souris.


----------



## tatouille (11 Mai 2006)

Céroce a dit:
			
		

> A un niveau plus bas que Carbon? Il va lire les registres du contrôleur USB de la souris ou quoi?
> 
> Non, c'est très improbable.
> 
> ...



hier

http://www.mainevent.com/articles.html
http://devworld.apple.com/dev/techsupport/develop/issue21/21simone.html
http://developer.apple.com/documentation/AppleScript/Conceptual/AppleEvents/dispatch_aes_aepg/chapter_4_section_2.html#//apple_ref/doc/uid/TP40001449-CH204-BABDBIGA

http://developer.apple.com/samplecode/MoreOSL/MoreOSL.html

on peut voir avec ça que
AppleScriptCarbonKit.framework  est à inventer

*AppleScript 1.3.4 SDK (DMG)

-> *1999-01-23

aujourd'hui
quand on créee une application carbon
dans IB tu peux ajouter un composant applescript


----------



## Céroce (11 Mai 2006)

tatouille a dit:
			
		

> aujourd'hui
> quand on créee une application carbon
> dans IB tu peux ajouter un composant applescript



Ce qui veut bien dire qu'il faut délibéremment choisir d'implémenter les AppleEvents dans ton appli Carbon, non? C'est très bien qu'on puisse le faire simplement sous IB, mais ça ne remet pas en cause mon explication. Je ne prétends pas que c'est la bonne, mais elle est meilleure que dire que ça se passe à un niveau plus bas que Carbon.

Tous les exemples que Joan nous a donné d'appli qui marchent sont des applis scriptables (iCal, Safari). A mon humble avis, pour simuler un clic, AppleScript passe un AppleEvent "Clic de souris au point x,y". Il faudrait donc que le destinataire du clic soit scriptable.


----------



## Anonyme (11 Mai 2006)

Céroce a dit:
			
		

> Tous les exemples que Joan nous a donné d'appli qui marchent sont des applis scriptables (iCal, Safari). A mon humble avis, pour simuler un clic, AppleScript passe un AppleEvent "Clic de souris au point x,y". Il faudrait donc que le destinataire du clic soit scriptable.


Précisément. Or une animation Flash (ou un lien dans une vidéo Quicktime) au sein d'une page HTML est perçue par AppleScript comme un groupe dont les différents composants restent indéterminés. Ainsi, il n'est tout bonnement pas possible d'utiliser le scriptage d'interface pour intervenir sur l'un des éléments de ce groupe (un bouton, une animation, une URL, etc.) Les autres méthodes évoquées (Cocoa/Carbon) dépassent de loin mes maigres compétences.


----------



## tatouille (11 Mai 2006)

Céroce a dit:
			
		

> Ce qui veut bien dire qu'il faut délibéremment choisir d'implémenter les AppleEvents dans ton appli Carbon, non? C'est très bien qu'on puisse le faire simplement sous IB, mais ça ne remet pas en cause mon explication. Je ne prétends pas que c'est la bonne, mais elle est meilleure que dire que ça se passe à un niveau plus bas que Carbon.
> 
> Tous les exemples que Joan nous a donné d'appli qui marchent sont des applis scriptables (iCal, Safari). A mon humble avis, pour simuler un clic, AppleScript passe un AppleEvent "Clic de souris au point x,y". Il faudrait donc que le destinataire du clic soit scriptable.



heu c'étaitdans ton sens c'était juste pour noter ironiquement le belle effort d'apple  en trois ans


----------



## Joan (11 Mai 2006)

DocEvil a dit:
			
		

> (un bouton, une animation, une URL, etc.)



tous ces éléments dont tu parles font partie du fichier swf... on est bien d'accord la dessus ??? une URL mais une URL dans un flash...

Donc si j'y suis bien lol faut que je me plonge dans cocoa... ???


----------



## Anonyme (11 Mai 2006)

Joan a dit:
			
		

> tous ces éléments dont tu parles font partie du fichier swf... on est bien d'accord la dessus ??? une URL mais une URL dans un flash...


Oui, c'est exactement ce dont je voulais parler.
En ce qui concerne Cocoa, je cède la place aux experts.


----------



## PA5CAL (11 Mai 2006)

Bonsoir

Il est possible de fabriquer un petit programme avec Xcode pour simuler le fonctionnement de la souris.

Je n'ai pas trop le temps de regarder en ce moment, mais en revanche j'ai déjà un début de piste.

Une recherche sur "*IOHIDPostEvent()*" "*NX_LMOUSEUP*" "*NX_LMOUSEDOWN*" et "*NX_MOUSEMOVED*" devrait vous permettre de trouver votre bonheur.


----------



## Joan (11 Mai 2006)

PA5CAL a dit:
			
		

> Bonsoir
> 
> Il est possible de fabriquer un petit programme avec Xcode pour simuler le fonctionnement de la souris.
> 
> ...



Super Merci Pascal c'est ce qui me faut des pistes, si vraiment j'y arrive pas je reposte.


----------



## PA5CAL (11 Mai 2006)

Ça y est  ! J'ai pu trouver un peu de temps, alors j'ai concocté un petit programme en C++, type ligne de commande, qui simule les clics souris. Il faut le compiler avec le framework IOKit.


```
/* Click (C) 2006 PA5CAL for MacGeneration
 * 
 *  Purpose: Simulate mouse events
 *  Syntax:
 *        click <x> <y> [l|2|r]
 *  Options:
 *        l = left button click (default)
 *        2 = left button double click
 *        r = right button click
 */

#include <ApplicationServices/ApplicationServices.h>
#include <IOKit/hidsystem/IOHIDShared.h>
#include <IOKit/IOKitLib.h>


class HIDService
{
private:
    io_connect_t event_driver;
    mach_port_t io_master_port;
public:
    HIDService();
    ~HIDService();
    kern_return_t Open();
    kern_return_t Close();
    void SendMouseEvent(int right, int down, SInt32 x, SInt32 y);
    void Click(int right, SInt32 x, SInt32 y);
    void DoubleClick(int right, SInt32 x, SInt32 y);
};

#pragma mark -
#pragma mark HIDService

HIDService::HIDService()
{
    event_driver = MACH_PORT_NULL;
    Open();
}

HIDService::~HIDService()
{
    Close();
}

kern_return_t HIDService::Close()
{
    kern_return_t r = KERN_SUCCESS;
    if (event_driver!=MACH_PORT_NULL)
        r = IOServiceClose(event_driver);
    event_driver = MACH_PORT_NULL;
    return r;
}

kern_return_t HIDService::Open()
{
    kern_return_t kr;
    mach_port_t ev, service;

    if (KERN_SUCCESS==(kr = Close())) {
        if (KERN_SUCCESS!=(kr = IOMasterPort(MACH_PORT_NULL, &io_master_port)))
            return kr;
        if (io_master_port!=MACH_PORT_NULL) {
            if (service = IOServiceGetMatchingService(kIOMasterPortDefault, 
                                        IOServiceMatching(kIOHIDSystemClass))) {
                kr = IOServiceOpen(service, mach_task_self(), 
                                    kIOHIDParamConnectType, &ev);
                IOObjectRelease(service);

                if (KERN_SUCCESS==kr)
                    event_driver = ev;
            }
        }
    }
    return kr;
}

void HIDService::SendMouseEvent(int right, int down, SInt32 x, SInt32 y)
{
    if (event_driver==MACH_PORT_NULL)
        return;

    int eventType;
    if (right){
        if (down)
            eventType = NX_RMOUSEDOWN;
        else
            eventType = NX_RMOUSEUP;
    } else {
        if (down)
            eventType = NX_LMOUSEDOWN;
        else
            eventType = NX_LMOUSEUP;
    }

    IOGPoint scrPos;
    scrPos.x = x;
    scrPos.y = y;

    NXEventData eventData;
    eventData.mouse.subType = NX_SUBTYPE_TABLET_POINT;
    eventData.mouse.subx = 0;
    eventData.mouse.suby = 0;
    eventData.mouse.click = 0;
    eventData.mouse.pressure = (down ? 255 : 0);
    eventData.mouse.tablet.point.x = x;
    eventData.mouse.tablet.point.y = y;
    eventData.mouse.tablet.point.buttons = 0x0000;
    eventData.mouse.tablet.point.tilt.x = 0;
    eventData.mouse.tablet.point.tilt.y = 0;
    eventData.mouse.tablet.point.deviceID = 0x81;

    (void)IOHIDPostEvent(event_driver, eventType, scrPos, &eventData,
                         kNXEventDataVersion, 0, kIOHIDSetCursorPosition);

    struct timespec rqtp = {0, 50000000};
    nanosleep(&rqtp, NULL);
}

void HIDService::Click(int right, SInt32 x, SInt32 y)
{
    SendMouseEvent(right, 1, x, y);
    SendMouseEvent(right, 0, x, y);
}

void HIDService::DoubleClick(int right, SInt32 x, SInt32 y)
{
    Click(right, x, y);
    Click(right, x, y);
}

#pragma mark -

void usage()
{
    printf("Click (C) 2006 PA5CAL for MacGeneration\n\n"
           "Purpose:\n"
           "\tSimulate mouse events\n"
           "Syntax:\n"
           "\tclick <x> <y> [l|2|r]\n"
           "Options:\n"
           "\tl = left button click (default)\n"
           "\t2 = left button double click\n"
           "\tr = right button click\n\n");
}

int main (int argc, char * const argv[])
{
    if (argc<3) {
        usage();
        return 0;
    }
    
    SInt32 x, y;
    x = atol(argv[1]);
    y = atol(argv[2]);

    int right = 0;
    int dbl = 0;
    if (argc>=4) {
        if (argv[3][0]=='r')
            right = 1;
        else
        if (argv[3][0]=='2')
            dbl = 1;
    }
    
    HIDService *hs;
    try {
        hs = new HIDService();
    }
    catch (const char *err) {
        printf("- failed -\n\n");
        delete hs;
        return 0;
    }
    
    if (dbl)
        hs->DoubleClick(right, x, y);
    else
        hs->Click(right, x, y);
    
    delete hs;
    
    return 0;
}
```


----------



## Joan (12 Mai 2006)

Merci enormement 

 je me penche dessus ce week-end et t'en donne très vite des nouvelles


----------



## Céroce (12 Mai 2006)

tatouille a dit:
			
		

> heu c'étaitdans ton sens c'était juste pour noter ironiquement le belle effort d'apple  en trois ans



Euh, excuse-moi, je ne sais pas ce qu'Apple a apporté à Carbon dans ce domaine, mais j'ai comme-qui-dirait la conviction que la priorité porte sur Cocoa chez Peupeul.


----------



## tatouille (12 Mai 2006)

Céroce a dit:
			
		

> Euh, excuse-moi, je ne sais pas ce qu'Apple a apporté à Carbon dans ce domaine, mais j'ai comme-qui-dirait la conviction que la priorité porte sur Cocoa chez Peupeul.



Apple a apporté à Carbon dans ce domaine

rien ou penuts ils comptaient certainement sur metrowerks ou des bonnes âmes 

 la priorité porte sur Cocoa chez Peupeul

oui c'est normal c'est L'API pour les Applications GUI et webObject
le reste c'est pour les trippés


----------



## PA5CAL (12 Mai 2006)

Céroce a dit:
			
		

> j'ai comme-qui-dirait la conviction que la priorité porte sur Cocoa chez Peupeul.


Il me semble bien effectivement que Cocoa est plus d'actualité que Carbon, qui a plutôt tendance maintenant à tomber en désuétude. Enfin c'est ce qui ressortais d'une doc d'Apple qui donnait des conseils sur la manière de mener des développements. Mais ça ne signifie pas que Carbon disparaîtra, ni même que que Cocoa n'aura pas de remplaçant dans un avenir plus ou moins proche...


----------



## tatouille (12 Mai 2006)

PA5CAL a dit:
			
		

> Il me semble bien effectivement que Cocoa est plus d'actualité que Carbon, qui a plutôt tendance maintenant à tomber en désuétude. Enfin c'est ce qui ressortais d'une doc d'Apple qui donnait des conseils sur la manière de mener des développements. Mais ça ne signifie pas que Carbon disparaîtra, ni même que que Cocoa n'aura pas de remplaçant dans un avenir plus ou moins proche...


disons que carbon pour des applications GUI il va falloir arreter
et bon nombre de methods systems de Carbon descendent dans le Foundation
(Foundation, CoreFoundation , Foundation kit)

ce qui est plus cohérent

"Carbon application that installed a *CFNotification* observer"

AppleEvent / *CFNotification*


----------



## Joan (19 Mai 2006)

Me revoila. Vous allez penser que je suis vraiment une bille. Pa5cal tu l'as testé ton prog ? il fonctionne ? je dois vraiment mal m'y prendre. J'ai compilé avec Xcode mais après j'ai un epu de mal à le faire fonctionner...


----------



## PA5CAL (19 Mai 2006)

Je l'ai testé, et ça fonctionne. J'arrive à déplacer le curseur de la souris, fermer des fenêtres, sélectionner des mots, etc.

Par exemple, quand j'exécute "./click 20 10", j'ouvre le menu "Pomme".

En revanche, je n'ai pas fait l'essai sur une fenêtre Flash.


----------



## PA5CAL (19 Mai 2006)

Pour faire l'essai dans XCode sans la ligne de commande, tu pourrais mettre en commentaire le début de la fonction _main()_ jusqu'à "_HIDService *hs;_" (exclus), et remplacer cette partie par :

```
SInt32 x=0;
    SInt32 y=0;
    int right = 0;
    int dbl = 0;
```

En exécutant le programme, le curseur de la souris doit se caler en haut à gauche de l'écran, et le menu "Pomme" doit s'ouvrir. Si ce n'est pas le cas, exécute à nouveau ce programme en pas-à-pas, pour voir si un appel système ne retourne pas une erreur.


----------



## Joan (19 Mai 2006)

Voila le message qui apparait :



> ZeroLink: unknown symbol '_kIOMasterPortDefault'
> 
> click has exited due to signal 6 (SIGABRT).


----------



## PA5CAL (19 Mai 2006)

C'est parce que tu n'as pas inclus, comme je l'indiquais, le framework *IOKit* dans la compilation.


----------



## tatouille (19 Mai 2006)

```
-framework IOKit
```

ou dans xcode ajoute le framework


----------



## Joan (20 Mai 2006)

Super Merci enormement à vous deux. Ca marche super...  

Moi qui pour l'instant ne connais que actionscript et javascript, cela m'a donné envi de me lancer dans le C++. Vraiment merci beaucoup cette experience me fait enfin "sauter le pas" moi qui étais un peu rétissant à la base, je ne le suis plus du tout du tout  

Je fais un teste pour flash et je vous tiens au courant...


----------



## Joan (21 Mai 2006)

Pour la suite des infos voila du neuf :

Ca fonctionne pour le flash voici d'ailleurs mon script :


```
on idle
       tell application "Terminal" to do shell script "/click/build/click 20 10"
       delay 3
       tell application "Terminal" to do shell script "/click/build/click 50 10"
       return 4
end idle
```

Clique sur le menu "Pomme" puis 3 secondes après sur "Finder" et recommance tout cela 4 secondes plus tard.

Par contre dans une fenêtre safari il faut que je lui donne 2 fois la commande : une première fois pour activer la fenêtre de safari et une deuxième pour que le clic soit effectif dans la fenêtre de safari (2 clics donc).

Y a t'il un moyen pour executer une commande du terminal avec la fenetre du terminal en tache de fond (en arriere plan donc) évitant un va et vient entre la fenêtre du terminal et celle de safari qui m'oblige à lui demander de faire 2 fois la commande du style :


```
on idle
       tell application "Terminal" to do shell script "/click/build/click 150 120"
       delay 1
       tell application "Terminal" to do shell script "/click/build/click 150 120"
       delay 1
       tell application "Terminal" to do shell script "/click/build/click 140 110"
       delay 1
       tell application "Terminal" to do shell script "/click/build/click 140 110"
       return 4
end idle
```

pour en fait n'avoir que deux clics dans la fenêtre de safari : un en 150,120 et l'autre en 140,110...


----------



## PA5CAL (21 Mai 2006)

Je ne suis pas un pro de l'AppleScript, mais je pense qu'on peut lancer le programme _click_ directement sans que _Terminal_ ne soit exécuté. Reste à connaître la syntaxe pour passer les deux paramètres de _click_ dans AppleScript.


----------



## tatouille (22 Mai 2006)

PA5CAL a dit:
			
		

> Je ne suis pas un pro de l'AppleScript, mais je pense qu'on peut lancer le programme _click_ directement sans que _Terminal_ ne soit exécuté. Reste à connaître la syntaxe pour passer les deux paramètres de _click_ dans AppleScript.



on idle
       do shell script "/click/build/click 150 120"
       do shell script "/click/build/click 150 120"
      ...
end idle
évite les delay en applescript et gere les task dans ton binaire
genre envoie un tableau de processus en argument


----------



## Joan (24 Mai 2006)

Ca fonctionne toujours mal. En fait j'ai l'impression qu'il lui faut deux clics à chaque fois que je veux qu'il fasse un clic à un endroit comme si il lui fallait un clic pour activer la fenêtre de safari et un deuxième pour aller cliquer à l'endroit que je lui demande. Pourtant certaine fois il clique bien dès la première fois au bon endroit ce qu'il fait qu'il clique deux fois en fait ce qui ne me convient pas...

Je vais tester en lui disant à la limite pour le premier clic de cliquer à un endroit de la fenêtre où il n'y pas de bouton ou d'animation flash, un endroit qui n'implique pas une action au clic comme ça s'il clic bon dès le premier coup pas de souci... c'est peutetre la solution je vous tient au courant...

Merci beaucoup en tout cas pour votre aide 

Rectif après teste 10 minutes après : et bein non ca fonctionne pas, il lui faut on dirait deux clics dans l'animation... le seul problème c'est que de temps en temps ca fonctionne avec un seul clic et donc les deux clics sont fonctionnel à un même endroit ce qui me plante ma démarche.

Troisième édition du post : il serait peutetre possible de lui dire qu'il execute plusieurs commande à la suite sans relancer le "do shel script" :

du style (ca reste dans la logique et non une ligne de code que je pense valide) :



> do shell script "commande 1 & commande 2 & commande 3..."



ou



> do shell script "commande 1" & "commande 2" & "commande 3"...


----------

