Exemple capturer et libérer la souris dans un écran

Partagez votre expérience de PureBasic avec les autres utilisateurs.
Shadow
Messages : 1413
Inscription : mer. 04/nov./2015 17:39

Exemple capturer et libérer la souris dans un écran

Message par Shadow »

Salut, j'ai fais ceci en me basant sur un code dans l'aide.
J'en suis plutôt satisfais, mais bien qu'il fonctionne très bien, ya surement moyen de faire plus simple.

J'attaque les écran, c'est aussi un bon moyen pour faire des jeux ou des applications mais c'est plus dur à gérer !

Le coté positif c'est que c'est le GPU qui gère tous ça donc beaucoup plus rapide.
Le coté négatif, c'est plus difficile à gérer, et qu'un seul écran à la fois autorisé et niveau évènements (clavier et souris), c'est moins fournie bien qu'il est le minimum nécessaire.

Jvé peut être faire un module pour rendre tous ça plus simple mais avant j'aurais besoin de vos retour sur ce code.
Hésitez pas à me dire se que vous en pensez !

Code : Tout sélectionner

;{ Explications:
; Exemple réalisé par Dieppedalle David, Alias Shadow.
; Merci à Naheulf pour m'avoir indiqué comment améliorer certaine petite partie du code (Des bricoles).

; Cet exemple-ci a été réalisé sur la base d'un exemple dans l'aide de PB pour réaliser celui-ci. (ReleaseMouse)
; Dans cet exemple-ci (Utilisation du processeur CPU et GPU seulement quand nécessaire.):
; 
; - Quand la souris entre dans l'écran, celle-ci est capturé.
; - Quand la souris sort de l'écran, celle-ci est libéré ou si le bouton #Boutton a été cliqué.
;   (C'est utile surtout si le bouton a le focus et si la touche espace est appuyée pour activer le bouton).
; - Quand la souris bouge dans l'écran, une image (Curseur souris) est déplacée à l'endroit de la souris ainsi que des infos sur sa position dans l'écran ainsi qu'un texte (L'écran est actualisé).
; 
; Pour optimiser tout ça un maximum, rien ne ce passe si:
; 
; La fenêtre n'est pas active (La fenêtre n'a pas le focus).
; La souris n'est pas sur la fenêtre (Rien ne se passe).
; La souris ne bouge pas dans l'écran (L'écran ne sera plus actualisé).
; La souris ne bouge pas sur la fenêtre (Pour les évènements de la fenêtre).
;}

;{ Variables pour l'écran.
Global PositionEcranX.i = 10  ; La position X de l'écran sur la fenêtre.
Global PositionEcranY.i = 10  ; La position Y de l'écran sur la fenêtre.
Global LargeurEcran.i = 780   ; La largeur de l'écran.
Global HauteurEcran.i = 530   ; La hauteur de l'écran.
Global WindowMouseX.i         ; Pour savoir la position X de la souris sur la fenêtre.
Global WindowMouseY.i         ; Pour savoir la position Y de la souris sur la fenêtre.
Global InScreen.b             ; Pour mémoriser si la souris est dans l'écran ou non, #False = 0, #True = 1.
Global Changement.b           ; #True (1) pour actualiser l'affichage, sinon #False (0) (Automatique).

; Pour mémoriser la position de la souris dans l'écran quand elle a bougé.
Global SaveSreenMouseX.i, SaveSreenMouseY.i

; Texte qui sera afficher sur l'écran suivant si la souris est dessus ou non.
Global TexteSourisHorsEcran.s = "La Souris n'est plus dans l'écran et a été relâchée !"
Global TexteSourisDansEcran.s = "La Souris est dans l'écran et a été capturée !"

; #True: L'écran sera redimensionné et sont contenu sera zoomé quand les dimensions de la fenêtre changeront.
; #False: Pas de redimensionnement de l'écran (par défaut).
RedimensionnementAuto.b = #False

; Définit les marges droite et basse dans la fenêtre, en pixels. (Pour prendre en compte la barre d'état (statusbar) par exemple). 
OffsetDroit.i = 0

; #PB_Screen_NoSynchronization: Désactive la synchronisation.
; #PB_Screen_WaitSynchronization: Active la synchronisation (Mode par défaut).
; #PB_Screen_SmartSynchronization: Active la synchronisation en essayant de préserver le temps processeur, lorsque le jeu est en mode plein écran.
; 
; Attendre la synchronisation permet d'avoir des inversions de buffers parfaites, sans 'tearing' (ancienne et nouvelle image à moitié visible)
; ou autres effets parasites car l'inversion est effectuée lorsque que le faisceau lumineux est en dehors de la partie visible de l'écran.
; De plus, l'inversion se fait à un rythme régulier, en relation avec sa fréquence de rafraîchissement, par exemple, pour un écran en 60 Hz
; l'inversion pourra se faire au maximum 60 fois par seconde (Donc on aura un rythme régulier de 60 images/secondes).
FlipMode.i = 0
;}

;{ DataSection
DataSection
  Curseur_Start:
  ; size : 310 bytes
  Data.q $0A1A0A0D474E5089,$524448490D000000,$150000000C000000,$0FAC720000000608,$414449FD000000B4,$010EBD928D9C7854
  Data.q $90780BC36F851051,$2942494227E25A8D,$B214489051A10AB4,$4B63572D80DE2AD4,$33D8CBBC689428A5,$D938A7F773FB59D9
  Data.q $55ECCCCF77CF77BD,$52BCAAFDF44670C6,$D1B140DCA08F5A85,$D84EE67843F3CBA7,$C574E681886E765A,$0092E2152A515400
  Data.q $12665BD70E9B2798,$6CD6CD6A3B177427,$0A40002710E93A3E,$C271E9BA408A417A,$83494FF96753DCC8,$8A2D610594800138
  Data.q $494A63304027E796,$EF3A55CE7662010A,$875F75E86C7EF44B,$727693CD040CEF84,$D350E65901ECA0C2,$D9988169124E333A
  Data.q $18F55907BB1DB450,$1B8AFBCD021BA2A4,$2301927FA207C815,$EEAB1FDDE2E5B800,$4A452F01B252E8BE,$C108707DEF12B296
  Data.q $99302F3C5D0F1FFD,$4549000000007B72
  Data.b $4E,$44,$AE,$42,$60,$82
  Curseur_End:
EndDataSection
;}

Enumeration
  #Window ; Fenêtre.
  #Boutton; Bouton.
  #CurseurSouris ; Curseur personnalisé de la souris.
EndEnumeration

UsePNGImageDecoder() ; Pour décoder les image Png (Utile pour l'image dans la DataSection).

; Charge une image en mémoire.
; '?Curseur_Start' est l'adresse de début de l'image dans la mémoire
; '?Curseur_End - ?Curseur_Start' est la taille de l'image.
CatchImage(#CurseurSouris, ?Curseur_Start, ?Curseur_End - ?Curseur_Start)

; Affiche un écran avec la souris pris en compte (Utile quand la souris est sur l'ecran).
Procedure DrawScreenWithMousse(MousseX.i, MousseY.i)
  
  ; Efface l'écran avec du gris.
  ClearScreen(RGB(220, 220, 220)) 
  
  ; Commence une opération de dessin sur l'écran.
  StartDrawing(ScreenOutput())
  
  ; Dessine le texte avec un fond transparent.
  DrawingMode(#PB_2DDrawing_Transparent)
  
  ; Dessine le texte au centre de la fenêtre.
  DrawText((LargeurEcran.i - TextWidth(TexteSourisDansEcran.s)) / 2, (HauteurEcran.i - TextHeight(TexteSourisDansEcran.s)) / 2, TexteSourisDansEcran.s, RGB(255,0,0))
  
  ; Texte de la position de la souris dans l'écran.
  TextePositionSouris.s = "X: " + Str(SaveSreenMouseX.i) + ", Y: " + Str(SaveSreenMouseY.i)
  
  ; Calcule la position X du texte suivant la position de la souris dans l'écran.
  If SaveSreenMouseX.i < LargeurEcran.i - 105
    TextePositionSourisX.i = SaveSreenMouseX.i + 12
  Else
    TextePositionSourisX.i = SaveSreenMouseX.i - TextWidth(TextePositionSouris.s)
  EndIf
  
  ; Calcule la position Y du texte suivant la position de la souris dans l'écran.
  If SaveSreenMouseY.i < HauteurEcran.i - 38
    TextePositionSourisY.i = SaveSreenMouseY.i + 22
  Else
    TextePositionSourisY.i = SaveSreenMouseY.i - TextHeight(TextePositionSouris.s) - 2
  EndIf
  
  ; Dessine le texte avec un fond transparent.
  DrawingMode(#PB_2DDrawing_Transparent)
  DrawText(TextePositionSourisX.i, TextePositionSourisY.i, TextePositionSouris.s, RGB(255, 150, 0))
  
  ; Dessine l'image du curseur de la souris à la position de la souris dans l'écran.
  DrawAlphaImage(ImageID(#CurseurSouris), MousseX.i, MousseY.i)
  
  ; Dessine une bordure qui représente la limite de capture de la souris dans l'écran.
  DrawingMode(#PB_2DDrawing_Outlined)
  Box(0, 0, LargeurEcran.i, HauteurEcran.i, RGB(255, 0, 0)); Crée une bordure verte pour indiquer la limite de capture et de libération de la souris.
  
  StopDrawing() ; Termine l'opération de dessin sur l'écran.
  FlipBuffers() ; Actualise l'affichage.
  
EndProcedure

; Affiche un écran sans la souris (Utile lorsque la souris n'est plus sur l'ecran).
Procedure DrawScreenNoMousse()
  
  ; Efface l'écran avec du gris.
  ClearScreen(RGB(220, 220, 220)) 
  
  ; Commence une opération de dessin sur l'écran.
  StartDrawing(ScreenOutput())
  
  ; Dessine le texte avec un fond transparent.
  DrawingMode(#PB_2DDrawing_Transparent)
  
  ; Dessine le texte au centre de la fenêtre.
  DrawText((LargeurEcran.i - TextWidth(TexteSourisHorsEcran.s)) / 2, (HauteurEcran.i - TextHeight(TexteSourisHorsEcran.s)) / 2, TexteSourisHorsEcran.s, RGB(0,0,255))
  
  ; Dessine une bordure qui représente la limite de libération de la souris dans l'écran.
  DrawingMode(#PB_2DDrawing_Outlined)
  Box(0, 0, LargeurEcran.i, HauteurEcran.i, RGB(0, 0, 255)); Crée une bordure verte pour indiquer la limite de capture et de libération de la souris.
  
  StopDrawing() ; Termine l'opération de dessin sur l'écran.
  FlipBuffers() ; Actualise l'affichage.
  
EndProcedure

; Gère la souris dans l'écran, capture et libère celle-ci quand nécessaire et gère l'affichage de l'écran.
Procedure MouseScreenManager()
  
  SreenMouseX.i = MouseX() ; Récupère la position X de la souris dans l'écran.
  SreenMouseY.i = MouseY() ; Récupère la position Y de la souris dans l'écran.
  
  ; Sauvegarde la position de la souris dans l'écran si elle a bougé.
  If SaveSreenMouseX.i <> SreenMouseX.i Or SaveSreenMouseY.i <> SreenMouseY.i
    SaveSreenMouseX.i = SreenMouseX.i
    SaveSreenMouseY.i = SreenMouseY.i
    Changement.b = #True ; Pour actualiser l'affichage.
  EndIf
  
  ; Gestion de la capture de la souris dans l'écran lorsque celle-ci entre ou sort de celui-ci...
  If InScreen.b = #True ; Si la souris est dans l'écran.
    
    ; Si la souris est sur les bords de l'écran (La limite verte).
    If SreenMouseX.i = 0 Or SreenMouseY.i = 0 Or SreenMouseX.i = LargeurEcran.i - 1 Or SreenMouseY.i = HauteurEcran.i - 1
      ReleaseMouse(#True) ; Libère la souris.
      InScreen.b = #False
      Changement.b = #True ; Pour actualiser l'affichage.
      
    Else                   ; Sinon la souris est dans l'écran, en gris.
      ExamineMouse()       ; Capture la souris tant qu'elle est dans l'écran.
      
    EndIf
    
    ; Sinon si la souris entre dans l'écran (à l'intérieur de La limite verte), car InScreen.b = #False.
  ElseIf WindowMouseX.i > PositionEcranX.i And WindowMouseX.i < PositionEcranX.i + LargeurEcran.i  - 1 And WindowMouseY.i > PositionEcranY.i And WindowMouseY.i < PositionEcranY.i + HauteurEcran.i - 1
    ReleaseMouse(#False)    ; Alors on capture la souris.
    MouseLocate(WindowMouseX.i - PositionEcranX.i, WindowMouseY.i - PositionEcranY.i) ; Change la position absolue (En pixels) de la souris dans l'écran courant, ici 0, 0 correspond donc à la position de l'écran en Y et Y. 
    InScreen.b = #True
    Changement.b = #True ; Pour actualiser l'affichage.
    
  EndIf 
  
  ; Gestion de l'affichage de l'écran ici:
  
  ; Si on doit actualiser l'affichage, il y a eu un changement, par exemple, si la souris à bougé sur l'écran ou autre chose.
  If Changement.b = #True
    
    ; Si la souris est dans l'écran.
    If InScreen.b = #True
      DrawScreenWithMousse(SaveSreenMouseX.i, SaveSreenMouseY.i) ; Affiche l'écran en prenant en compte la souris, ici c'est quand la souris est sur l'écran.
    Else
      DrawScreenNoMousse() ; Affiche l'écran sans prendre en compte la souris, ici c'est quand la souris n'est plus sur l'écran.
    EndIf
    
  EndIf
  
  Changement.b = #False ; Pour ne plus actualiser l'affichage.
  
EndProcedure

; Initialisation du monde 2D.
InitSprite() ; Initialise la gestion des sprites.
InitMouse()  ; Initialise la gestion de la souris.

; Ouverture de la fenêtre.
OpenWindow(#Window, 0, 0, 800, 600,"Capture/Libération de la souris", #PB_Window_ScreenCentered | #PB_Window_SystemMenu)
ButtonGadget(#Boutton, WindowWidth(#Window) - 130, WindowHeight(#Window) - 35, 120, 24, "Cliquez !") ; BoutonGadget.

; Ouvre un Ecran dans la fenêtre.
OpenWindowedScreen(WindowID(#Window), PositionEcranX.i, PositionEcranY.i, LargeurEcran.i, HauteurEcran.i, RedimensionnementAuto.b, OffsetDroit.i, FlipMode.i)
DrawScreenNoMousse() ; Pour afficher l'écran sans la souris la première fois.

; Gestion de la boucle de l'écran.
Repeat
  
  ; Gestion de la boucle de la fenêtre.
  Repeat
    
    ; Un évènement a eut lieu sur la fenêtre, mais lequel ?
    Event.i  = WindowEvent()
    
    ; Cherchons quel évènement c'est...
    Select Event.i
        
      Case #PB_Event_Gadget ; C'est un évènement d'un Gadget.
        
        Select EventGadget() ; Sur quel Gadget est-ce ?
            
          Case #Boutton ; Si on a cliqué sur le Bouton '#Boutton'.
            ReleaseMouse(#True) ; Libère la souris.
            InScreen.b = #False ; Pour indiquer que la souris n'est plus dans l'écran (Très important).
            DrawScreenNoMousse() ; Pour afficher l'écran conrespondant sans la souris.
            MessageRequester("Information...","   Tu as cliqué sur le Bouton !", 64)
            
        EndSelect 
        
      Case #PB_Event_DeactivateWindow ; Qand la fenêtre pert le focus, par exemple avec le racourcis clavier Alt + Tab.
        ReleaseMouse(#True) ; Libère la souris.
        InScreen.b = #False ; Pour indiquer que la souris n'est plus dans l'écran (Très important).
        DrawScreenNoMousse(); Pour afficher l'écran conrespondant sans la souris.
        
      Case #PB_Event_CloseWindow ; Si la fenêtre a été fermée.
        End                      ; Termine le programme ici.
        
    EndSelect
    
    ; Si la fenêtre est active.
    If GetActiveWindow() = #Window
      
      ; Récupère la position de la souris sur la fenêtre.
      WindowMouseX.i = WindowMouseX(#Window)
      WindowMouseY.i = WindowMouseY(#Window)
      
      ; Si la souris sur la fenêtre bouge.
      If SaveWindowMouseX.i <> WindowMouseX.i Or SaveWindowMouseY.i <> WindowMouseY.i
        
        SaveWindowMouseX.i = WindowMouseX.i ; Sauvegarde la position X de la souris sur la fenêtre.
        SaveWindowMouseY.i = WindowMouseY.i ; Sauvegarde la position Y de la souris sur la fenêtre
        
        ; Affichage de la position de la souris dans la fenêtre.
        SetWindowTitle(#Window, "Capture/Libération de la souris: X = " + Str(SaveWindowMouseX.i) + " Y = "+ Str(SaveWindowMouseY.i))
        
      EndIf
      
    EndIf
    
  Until Event.i =  0 ; Si il n'y a plus d'évènements sur la fenêtre.
  
  ; Si la fenêtre est active et si la souris est sur la fenêtre.
  If GetActiveWindow() = #Window And WindowMouseX.i > -1 And WindowMouseY.i > -1 And 
    MouseScreenManager() ; Gère la souris dans l'écran.
  Else
    Delay(1) ; Attend un peu si la fenêtre n'est pas active ou si la souris n'est pas sur la fenêtre. 
  EndIf
  
ForEver
Dernière modification par Shadow le sam. 05/mars/2022 20:45, modifié 4 fois.
Processeur: Intel Core I7-4790 - 4 Cœurs - 8 Thread: 3.60 Ghz.
Ram: 32 GB.
Disque: C: SDD 250 GB, D: 3 TB.
Vidéo: NVIDIA GeForce GTX 960: 2 GB DDR5.
Écran: Asus VX248 24 Pouces: 1920 x 1080.
Système: Windows 7 64 Bits.

PureBasic: 5.60 x64 Bits.
Avatar de l’utilisateur
falsam
Messages : 7324
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: Exemple capturer et libérer la souris dans un écran

Message par falsam »

Bonjour Shadow.

Que ce soit avec le pad de mon ordinateur portable ou avec ma souris, le curseur n'accède pas au screen.
Configuration : Windows 11 Famille 64-bit - PB 6.20 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Avatar de l’utilisateur
falsam
Messages : 7324
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: Exemple capturer et libérer la souris dans un écran

Message par falsam »

Je viens de comprendre pourquoi. La mise à l'échelle de l'affichage est réglée par défaut à 125%.

:idea: En cochant la case Enable DPI aware exécutable dans les options de compilation, le souci est presque réglé.

la taille de l'OpenWindowedScreen est réduite :wink:
Configuration : Windows 11 Famille 64-bit - PB 6.20 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Shadow
Messages : 1413
Inscription : mer. 04/nov./2015 17:39

Re: Exemple capturer et libérer la souris dans un écran

Message par Shadow »

Bonjour falsam,

Merci pour ton test.
le souci est presque réglé.
C'est à dire ?

J'ai étudié et fais ça le plus propre possible pour que normalement ça marche.
J'attends des retours pour éventuellement améliorer etc...
Processeur: Intel Core I7-4790 - 4 Cœurs - 8 Thread: 3.60 Ghz.
Ram: 32 GB.
Disque: C: SDD 250 GB, D: 3 TB.
Vidéo: NVIDIA GeForce GTX 960: 2 GB DDR5.
Écran: Asus VX248 24 Pouces: 1920 x 1080.
Système: Windows 7 64 Bits.

PureBasic: 5.60 x64 Bits.
Avatar de l’utilisateur
falsam
Messages : 7324
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: Exemple capturer et libérer la souris dans un écran

Message par falsam »

Shadow a écrit : sam. 05/mars/2022 17:37C'est à dire ?
Ha mince je n'ai pas du m'exprimer correctement !

Alors je reprend :
1 - Ton CODE NE FONCTIONNE PAS si l'échelle d'affichage de windows est supérieur à 100%. De plus en plus, 125% est la règle par défaut.

2 - Le souci peut être régler en activant la gestion des DPI dans les options de compilation.
MAIS dans ce cas ton screen devient plus petit.
Configuration : Windows 11 Famille 64-bit - PB 6.20 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Shadow
Messages : 1413
Inscription : mer. 04/nov./2015 17:39

Re: Exemple capturer et libérer la souris dans un écran

Message par Shadow »

Ok c'est activé pour moi mais ça changera rien pour vous si je l'active ^^
Processeur: Intel Core I7-4790 - 4 Cœurs - 8 Thread: 3.60 Ghz.
Ram: 32 GB.
Disque: C: SDD 250 GB, D: 3 TB.
Vidéo: NVIDIA GeForce GTX 960: 2 GB DDR5.
Écran: Asus VX248 24 Pouces: 1920 x 1080.
Système: Windows 7 64 Bits.

PureBasic: 5.60 x64 Bits.
Répondre