Guide de migration SDL 1.2 vers SDL 2

Ryan Gordon

Traducteur

Alexandre Laurent

La SDL 2 est maintenant disponible. Les développeurs de la bibliothèque nous proposent ce document pour nous aider à passer de l'ancienne à la nouvelle version.

Commentez

- Titre : Guide de migration SDL 1.2 vers SDL 2

- Auteur : Ryan Gordon

- Traducteur : Alexandre Laurent

- Parution : 12 décembre 2013

- Licence : Le contenu de cet article est rédigé par Ryan Gordon et est mis à disposition selon les termes de la Licence Creative Commons Attribution 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Sommaire

- I. Introduction

- I-A. Aperçu des nouvelles fonctionnalités

- I-B. Besoin de plus d'informations

- II. Transition de la SDL 1.2 à la SDL 2

- II-A. Quelques vérités générales

- II-B. Vidéo

- II-B-1. Si votre jeu ne souhaite qu'afficher des images entièrement rendues à l'écran

- II-B-2. Si votre jeu nécessite d'afficher des surfaces à l'écran

- II-B-3. Si votre jeu doit faire les deux

- II-B-4. D'autres notes sur le moteur de rendus

- II-C. OpenGL

- II-D. Entrées

- II-E. Événements

- II-F. Audio

- II-G. Joysticks

- II-H. Threads

- II-I. CD Audio

- II-J. Plateformes mortes

- II-K. Plateformes mobiles

- II-L. RWops

- II-M. Bibliothèques supplémentaires

- II-N. Sommaire des choses renommées ou remplacées

- II-O. Autres choses

- III. Remerciements

I - Introduction

Après de nombreuses années de développement, la SDL 2 est enfin finalisée.

Nous en sommes fiers et nous aimerions voir les jeux utilisant SDL 1.2 évoluer immédiatement. Comme cela peut être une tâche intimidante, ce document est une description pour migrer vers la nouvelle version. Nous parions que vous trouverez cela pas aussi dur que vous ne le pensiez, et à plusieurs reprises, vous allez soit remplacer les appels de fonctions avec leur équivalence, soit enlever les différents hacks vous permettant de gérer les différentes versions 1.2.

Nous pensons que vous serez très heureux avec la SDL 2.0, que ce soit pour les nouvelles fonctionnalités ou la meilleure expérience par rapport à la SDL 1.2. Ce document n'essaie pas de couvrir toutes les fantastiques nouvelles fonctionnalités de la SDL 2 - et il y en a plein -, mais seulement les choses dont vous avez besoin pour que cela fonctionne immédiatement. Une fois que vous avez porté votre code et vérifié les nouvelles fonctionnalités, vous allez sûrement vouloir en ajouter certaines dans votre application.

I-A - Aperçu des nouvelles fonctionnalités

Voici les fonctionnalités les plus importantes dans la SDL 2.0 :

- accélération matérielle 3D complète ;

- support d'OpenGL 3.0 et supérieur avec de nombreux profils (core, compatibility, debug, robust, etc.) ;

- support d'OpenGL ES ;

- support de multiples fenêtres ;

- support de multiples moniteurs ;

- support de multiples périphériques audio ;

- support d'Android et iOS ;

- bibliothèque de rendus simple 2D pouvant utiliser Direct3D, OpenGL, OpenGL ES ou un rendu logiciel ;

- retour de force disponible sur Windows, Mac OS X et Linux ;

- support de XInput et XAudio 2 pour Windows ;

- opérations atomiques ;

- gestion de l'énergie (indique l'autonomie de la batterie, etc.) ;

- fenêtre aux formes personnalisées ;

- audio 32 bits (entier et virgule flottante) ;

- bibliothèque simplifiée pour les manettes de jeu (la bibliothèque Joystick est toujours là !) ;

- support du toucher (multi-touch, mouvements, etc.) ;

- meilleur support du mode plein écran ;

- meilleur support du clavier (scancodes VS keycodes, etc.) ;

- boîtes de messages ;

- support du presse-papier ;

- support basique du glisser/déposer ;

- support correct des entrées Unicode et IME ;

- une macro puissante d'assertion ;

- licence zlib à la place de la LGPL ;

- de nombreux ennuis ont disparu depuis la SDL 1.2 ;

- de nombreuses autres choses !

La page d'introduction possède une liste encore plus étendue sur les fonctionnalités offertes par la SDL (incluant les anciennes fonctionnalités de la SDL 1.2).

I-B - Besoin de plus d'informations

Les meilleures places pour avoir des informations sont :

- le wiki officiel :)

- les tests inclus dans la SDL, dans le répertoire test (navigation en ligne) ;

- la liste de diffusion SDL.

II - Transition de la SDL 1.2 à la SDL 2

II-A - Quelques vérités générales

Il n'y a pas de couche de compatibilité implémentée dans la SDL 2. Si nous sommes passés à la version 2.0, c'est que nous avons modifié ou supprimé les anciennes fonctions là où cela était nécessaire. Si vous compilez votre programme SDL 1.2 avec les entêtes SDL 2.0, la compilation échouera certainement. Ce document va essayer de vous guider à travers les changements les plus importants et ceux qui sont susceptibles de vous poser le plus de problèmes.

Il n'y a pas de SDL_main ! Bon, d'accord, il y est et il fait ce qu'il a toujours été censé faire : être un petit morceau de code qui cache les différences entre main() et WinMain() pour Windows. Il n'y a pas de code d'initialisation à l'intérieur et il est complètement optionnel. Cela signifie que vous pouvez utiliser la SDL sans modifier votre ligne principale, ce qui est bien pour les modules ou les langages de script ayant la SDL pour module. Tout ce qui se trouvait dans le SDL_main de la version 1.2 est maintenant dans SDL_Init() auquel il appartient.

Il n'y a plus de parachute SDL. Ce qui était appelé SDL_INIT_NOPARACHUTE est à présent un seul et unique état. Cela aurait posé des problèmes si un autre thread que le principal avait crashé et il aurait interféré avec les applications installant leur propre gestionnaire de signaux/exceptions. L'inconvénient est que quelques plateformes ne quittent pas correctement le mode plein écran lorsqu'elles plantent. Vous devez installer votre propre gestionnaire de plantage, ou appeler SDL_Quit() dans une fonction atexit() ou pas, si ce n'est pas un problème pour vous. Notez que pour les plateformes Unix, la SDL continue de capturer les signaux SIGINT et de les faire correspondre à l'événement SDL_QUIT.

II-B - Vidéo

La bibliothèque vidéo est la plus impactée par les changements. Les besoins ont énormément changé depuis la conception de la bibliothèque SDL dans la fin des années 90. Pour gérer les architectures modernes et les fonctionnalités des systèmes, nous avons presque entièrement remplacé l'ancienne bibliothèque.

Ne vous inquiétez pas, la nouvelle bibliothèque est superbe et une fois que vous aurez compris ce qui a changé, vous allez être très content avec les nouvelles fonctionnalités qu'elle peut apporter à votre jeu SDL 1.2. Nous allons discuter de cela plus tard.

La bonne nouvelle : si votre jeu utilisait OpenGL, vous n'avez probablement presque rien à faire : changer une poignée d'appels pour les remplacer par leur fonction SDL 2 équivalente et vous avez fini.

Pour les graphismes 2D, SDL 1.2 offrait un concept appelé « surfaces », qui n'était qu'un paquet de pixels. L'écran lui-même était une « surface », si vous faisiez un rendu logiciel 2D. Nous fournissions les fonctions pour copier (« blit ») les pixels entre les surfaces en convertissant le format au besoin. Vous étiez presque toujours en train de travailler sur le CPU et la mémoire vive, et non pas sur le GPU et la mémoire vidéo. SDL 2 apporte des changements à cela ; vous allez maintenant avoir presque toujours l'accélération matérielle et la bibliothèque a été modifiée pour correspondre à ce changement.

Si vous avez un jeu 2D, vous avez une chance de correspondre à l'un de trois cas présentés. Nous allons les voir, mais premièrement, commençons par une introduction.

Vous souvenez-vous de SDL_SetVideoMode() ? La fonction a complètement disparu. La SDL 2 vous permet d'avoir plusieurs fenêtres, donc l'ancienne fonction n'a plus aucune raison d'être.

Vous aviez donc quelque chose comme :

SDL_WM_SetCaption("Ma fenêtre de jeu", "jeu");
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 0, SDL_FULLSCREEN | SDL_OPENGL);

Qui correspond maintenant à :

SDL_Window *screen = SDL_CreateWindow("Ma fenêtre de jeu",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
640, 480,
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL);

Vous pouvez voir que cela correspond beaucoup à la version 1.2. La différence est que vous pouvez avoir plusieurs fenêtres (si vous le souhaitez) et vous pouvez mieux les contrôler. La fonction SDL_WM_SetCaption() est partie, car nous voulions permettre à chacune des fenêtres d'avoir son propre titre (que vous pouvez changer par la suite avec SDL_SetWindowTitle()SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_CENTERED) et nous souhaitions laisser la possibilité de spécifier une position de fenêtre (ou, dans ce cas, SDL_WINDOWPOS_UNDEFINED, car cela nous importe peu où elle apparaîtra. SDL_WINDOWPOS_CENTERED est aussi un bon choix).

Encore mieux, la SDL 2 permet à l'utilisateur de choisir le moniteur qui affichera la fenêtre : en effet, la bibliothèque vous permet de gérer plusieurs moniteurs. Ne vous en préoccupez pas pour le moment.

Donc, maintenant que votre fenêtre est revenue à l'écran, parlons de stratégie. La SDL 2 possède toujours des SDL_Surface, mais ce que vous souhaitez, si possible, c'est d'utiliser le nouveau SDL_Texture. Les surfaces sont toujours stockées dans la mémoire vive et sont toujours gérées par le CPU, donc nous souhaitons nous dépatouiller de là. SDL 2 possède une nouvelle bibliothèque de rendus. Elle a été développée pour un usage simple dans les jeux 2D, mais surtout, pour transférer ce rendu logiciel en mémoire vidéo et sur le GPU. Et même si vous souhaitez simplement l'utiliser pour obtenir quelque chose à l'écran, elle apporte des avantages intéressants : si possible, elle utilisera OpenGL ou DirectX en fond, ce qui signifie que vous obtiendrez des copies plus rapides, une surcouche de Steam fonctionnelle et le redimensionnement sans surcoût.

L'initialisation ressemble à cela.

SDL_SetVideoMode() devient SDL_CreateWindow() comme nous l'avons dit précédemment. Mais que devons-nous indiquer comme résolution ? Si votre jeu possédait ces valeurs en brut, par exemple 640 x 480, vous étiez sûrement dans un cas où les moniteurs ne pouvaient pas utiliser cette résolution en plein écran et en mode fenêtré, votre jeu ressemblait à un timbre-poste sur les très grands écrans. Il y a une meilleure solution dans la SDL 2.

Nous n'appelons plus SDL_ListModes(). Il y a une fonction équivalente dans la SDL 2 (appeler SDL_GetDisplayMode() dans une boucle, SDL_GetNumDisplayModes() fois), mais à la place nous allons utiliser la nouvelle fonctionnalité appelée « bureau plein écran »(« fullscreen desktop »), qui indique à la SDL « donne-moi l'intégralité de l'écran et ne change pas la résolution ». Pour notre hypothétique jeu en 640 x 480, le code pourra être :

SDL_Window *sdlWindow = SDL_CreateWindow(title,
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
0, 0,
SDL_WINDOW_FULLSCREEN_DESKTOP);

Notez que nous n'avons spécifié ni 640 ni 480… le mode bureau plein écran vous donne l'intégralité de l'écran et ignore les dimensions que vous spécifiez. La fenêtre de jeu devrait arriver immédiatement, sans attendre le changement de résolution de l'écran et nous allons utiliser le GPU pour redimensionner la taille du bureau, ce qui tend à être plus rapide et plus propre que la simulation d'une résolution par un écran LCD. Bonus supplémentaire : aucune de vos fenêtres en arrière-plan n'est redimensionnée.

Maintenant, nous avons besoin d'un contexte de rendu :

SDL_Renderer *renderer = SDL_CreateRenderer(sdlWindow, -1, 0);

Un moteur de rendus cache les détails sur la façon dont les sprites sont dessinés sur la fenêtre. Il peut utiliser Direct3D, OpenGL, OpenGL ES ou les surfaces logicielles, suivant ce que le système peut faire ; votre code ne change pas selon ce que la SDL utilise (bien que vous êtes invité à forcer l'un des moteurs de rendus plutôt qu'un autre). Si vous souhaitez essayer de forcer la synchronisation verticale pour réduire l'effet de déchirement, vous pouvez utiliser SDL_RENDER_PRESENTVSYNC à la place du zéro pour le troisième paramètre. Vous ne devez pas créer une fenêtre avec l'option SDL_WINDOW_OPENGL ici. Si SDL_CreateRenderer() décide qu'il doit utiliser OpenGL, il mettra à jour la fenêtre pour vous.

Maintenant que vous comprenez comment cela fonctionne, vous pouvez aussi faire tout cela avec la fonction SDL_CreateWindowAndRenderer(), si vous ne souhaitez rien de particulier :

SDL_Window *sdlWindow;
SDL_Renderer *sdlRenderer;
SDL_CreateWindowAndRenderer(0, 0, SDL_WINDOW_FULLSCREEN_DESKTOP, &sdlWindow, &sdlRenderer);

En présumant que ces fonctions n'ont pas échoué (vérifiez toujours les NULL), vous êtes prêt pour dessiner à l'écran. Commençons par le nettoyer en noir :

SDL_SetRenderDrawColor(sdlRenderer, 0, 0, 0, 255);
SDL_RenderClear(sdlRenderer);
SDL_RenderPresent(sdlRenderer);

Cela fonctionne comme vous l'imaginez ; dessine en noir (r,g,b tout à zéro, opacité complète), nettoyage de l'intégralité de la fenêtre, affichage de la fenêtre nettoyée à l'écran. C'est tout, si vous utilisiez SDL_UpdateRect() ou SDL_Flip() pour obtenir vos données à l'écran, le moteur de rendus utilise SDL_RenderPresent().

Une chose de plus à initialiser. Comme nous utilisons SDL_WINDOW_FULLSCREEN_DESKTOP, nous ne connaissons toujours pas la taille de la fenêtre sur laquelle on dessine. Heureusement, nous n'en avons pas besoin. Une chose bien dans la version 1.2 était que vous pouviez dire « Je souhaite une fenêtre de résolution 640 x 480 et je ne me préoccupe pas de comment vous l'obtenez », même si cela sous-entend de centrer la fenêtre de votre part.

Pour la SDL 2.0, le moteur de rendus vous permet de le faire…

SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");  // permet d'obtenir les redimensionnements plus doux.
SDL_RenderSetLogicalSize(sdlRenderer, 640, 480);

… et cela fera la bonne chose pour vous. C'est pratique dans le fait que vous pouvez changer la taille logique du rendu pour obtenir différents effets, mais l'utilisation principale est : à la place d'essayer de faire que le système travaille avec la taille de rendu, nous pouvons maintenant faire que la taille de rendu travaille avec le système. Sur mon écran 1920 x 1200, cette application pense qu'elle communique actuellement avec une résolution de 640 x 480, mais la SDL utilise le GPU pour agrandir tous les pixels. Notez que les ratios entre 640 x 480 et 1920 x 1200 ne sont pas les mêmes : la SDL prend aussi soin de cela en agrandissant autant que possible et en rajoutant des bandes noires pour ce qu'il reste.

Maintenant, nous sommes vraiment prêts pour afficher quelque chose.

II-B-1 - Si votre jeu ne souhaite qu'afficher des images entièrement rendues à l'écran

Un exemple de jeu de ce genre serait Doom, ou Duke Nukem 3D, ou plein d'autres. L'application souhaite dessiner tous les pixels elle-même et obtenir cet ensemble de pixels sur l'écran avec une grosse copie efficace.

Pour cela, nous souhaitons une unique SDL_Texture qui représentera notre écran. Commençons par en créer une pour notre jeu en 640 x 480 :

sdlTexture = SDL_CreateTexture(sdlRenderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
640, 480);

Cela correspond à une texture sur le GPU. Le plan d'action est de finir chaque image en uploadant les pixels dans la texture, dessinant la texture sur la fenêtre et afficher la fenêtre sur l'écran. SDL_TEXTUREACCESS_STREAMING indique à la SDL que le contenu de la texture changera souvent.

Avant, vous aviez probablement une SDL_Surface pour l'écran dans laquelle votre application faisait son rendu, puis vous appeliez SDL_Flip() pour l'envoyer à l'écran. Maintenant, vous pouvez créer une SDL_Surface qui est toujours en RAM à la place d'utiliser celle que vous auriez eue avec SDL_SetVideoMode(), ou vous pouvez juste allouer un bloc de pixels avec malloc() pour écrire à l'intérieur. Idéalement vous écrivez dans un tampon de pixels RGBA, mais si vous avez besoin d'effectuer une conversion, cela fonctionne aussi.

extern Uint32 *myPixels;  // peut-être que c'est surface->pixels, ou un tampon alloué avec malloc(), ou n'importe quoi d'autre.

À la fin de l'image, nous souhaitons uploader la texture comme suit :

SDL_UpdateTexture(sdlTexture, NULL, myPixels, 640 * sizeof (Uint32));

Cela mettra à jour vos pixels dans la mémoire GPU. Ce NULL peut être une sous-région si vous souhaitez jouer avec les « dirty rectangles », mais la chance est que le matériel moderne peut changer l'intégralité du tampon de rendu sans problème. Le dernier argument est pour le décalage - le nombre d'octets entre le début d'une ligne et la prochaine - et comme nous avons un tampon RGBA linéaire dans cet exemple, le décalage est simplement de 640 fois 4 (r,g,b,a).

Maintenant, affichons cette texture à l'écran :

SDL_RenderClear(sdlRenderer);
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
SDL_RenderPresent(sdlRenderer);

C'est tout. SDL_RenderClear() efface le tampon de rendu vidéo existant (dans ce cas, disons que la surcouche Steam a écrit par-dessus depuis l'image précédente), SDL_RenderCopy() déplace le contenu de la texture pour la placer dans le tampon de rendu vidéo (et grâce à SDL_RenderSetLogicalSize(), elle va être redimensionnée/centrée comme si le moniteur était de 640 x 480 pixels) et SDL_RenderPresent() affiche le tout sur l'écran.

II-B-2 - Si votre jeu nécessite d'afficher des surfaces à l'écran

Ce scénario décrit un jeu SDL 1.2 chargeant des graphismes à partir du disque dur dans des SDL_SurfaceSDL_HWSURFACE, possiblement en essayant de les envoyer vers la mémoire vidéo avec SDL_HWSURFACE. Vous les chargez une fois et vous les affichez encore et encore sur le tampon de rendu, mais à part cela, elles ne changent jamais. Un simple jeu de plateformes en 2D agit comme cela. Si vous pensez à vos surfaces comme des « sprites », et non comme des tampons de pixels, alors cette section est certainement pour vous.

Vous pouvez construire les textures (les surfaces qui sont sur le GPU) individuellement comme nous le faisions pour la grande texture :

sdlTexture = SDL_CreateTexture(sdlRenderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STATIC,
myWidth, myHeight);

Ce qui produit ce que vous pensez. Nous utilisons SDL_TEXTUREACCESS_STATIC, car nous n'allons uploader les textures qu'une seule fois. Une solution plus pratique serait :

sdlTexture = SDL_CreateTextureFromSurface(sdlRenderer, mySurface);

Utilisez cela et vous chargez vos SDL_Surface comme d'habitude, mais cette fois-ci, vous créez une texture à partir de la surface. Une fois que vous avez la SDL_Texture, vous pouvez libérer la surface.

À ce moment, votre jeu SDL 1.2 possédait un ensemble de SDL_Surface qu'il voudrait afficher avec SDL_BlitSurface() à l'écran pour compléter le tampon d'image final et éventuellement appeler SDL_Flip() pour le faire apparaître à l'écran. Pour la SDL 2.0, vous avez un ensemble de SDL_Texture, que vous souhaitez faire afficher à l'aide du moteur de rendus avec SDL_RenderCopy() pour compléter le tampon d'image final et éventuellement appeler SDL_RenderPresent() pour le faire apparaître à l'écran. C'est aussi simple. Si ces textures ne nécessitent jamais de modifications, vous allez aussi avoir un meilleur taux de rafraîchissement.

II-B-3 - Si votre jeu doit faire les deux

Les choses deviennent légèrement plus compliquées lorsque vous souhaitez afficher des surfaces et modifier des pixels individuellement dans le tampon de rendu. Les allers-retours - lire les données des textures - peuvent être énormément chers ; généralement vous souhaitez n'envoyer les données que dans un sens. Vous avez intérêt, dans ce cas, à garder tout sur le CPU jusqu'à l'affichage final sur l'écran et nous allons donc combiner les deux techniques précédentes.

La bonne nouvelle : la SDL_Surface de la SDL 1.2 est toujours là. Donc, changez votre surface d'écran de ça :

SDL_Surface *screen = SDL_SetVideoMode(640, 480, 32, 0);

… à ça …

// si tout cet hexadécimal vous effraie, allez voir SDL_PixelFormatEnumToMasks() !
SDL_Surface *screen = SDL_CreateRGBSurface(0, 640, 480, 32,
0x00FF0000,
0x0000FF00,
0x000000FF,
0xFF000000);
SDL_Texture *sdlTexture = SDL_CreateTexture(sdlRenderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
640, 480);

… et continuez de copier les choses et de jouer avec les pixels comme avant, complétant votre tampon d'image final dans cette SDL_Surface. Une fois que vous êtes prêt pour afficher ces pixels à l'écran, faites exactement ce que nous faisions dans le premier scénario :

SDL_UpdateTexture(sdlTexture, NULL, screen->pixels, screen->pitch);
SDL_RenderClear(sdlRenderer);
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
SDL_RenderPresent(sdlRenderer);

Notez que la création de texture peut être coûteuse et limitée : n'appelez pas SDL_CreateTextureFromSurface() à chaque image. Initialisez une texture et une surface et mettez à jour la texture à partir de la surface.

Il y a d'autres fonctionnalités disponibles dans le moteur de rendus, certaines d'entre elles peuvent remplacer votre code : le redimensionnement, l'affichage de lignes, etc. Si vous lisez cette section, car vous avez des besoins simples au-delà de la copie de surfaces, vous allez pouvoir arrêter d'envoyer les pixels un à un et vous placer sur le GPU, ce qui donnera à votre programme un important gain en vitesse et simplifiera certainement votre code.

II-B-4 - D'autres notes sur le moteur de rendus

Vous pouvez faire de simples effets avec le moteur de rendus sans avoir besoin de manipuler les pixels. Certains de ces effets étaient disponibles avec les surfaces de la SDL 1.2.

- Couleur alpha : SDL_Color contient maintenant un quatrième composant : le composant alpha. Votre code SDL 1.2 gérant les SDL_Color ne doit peut-être pas copier/définir cette valeur (qui était nommée unused). Dans la SDL 2.0, vous devez le faire.

- Le mélange alpha : utilisez SDL_SetSurfaceAlphaMod() et SDL_SetTextureAlphaMod()SDL_SetAlpha() à la place de SDL_SetAlpha(). Le mélange alpha sur les surfaces peut être désactivé avec SDL_SetSurfaceBlendMode() et sur les textures avec SDL_SetTextureBlendMode().

- La couleur-clé : lors de l'appel à SDL_SetColorKey()SDL_TRUESDL_SRCCOLORKEY, vous devez passer SDL_TRUE à la place de SDL_SRCCOLORKEY.

- Modulation de couleur : quelques moteurs de rendus supportent maintenant une altération globale des couleurs (srcC = srcC * color), allez voir SDL_SetTextureColorMod() pour les détails.

II-C - OpenGL

Si vous utilisiez directement OpenGL, votre migration sera très simple. Changez votre appel à la fonction SDL_SetVideoMode() en SDL_CreateWindow() suivi par SDL_GL_CreateContext()SDL_GL_SwapBuffers() et votre SDL_GL_SwapBuffers() par SDL_GL_SwapWindow(). Tous les appels à OpenGL sont exactement les mêmes.

Si vous utilisiez SDL_GL_SetAttributes(SDL_SWAP_CONTROL,x)(SDL_SWAP_CONTROL,x), cela a changé. Il y a maintenant une fonction SDL_GL_SetSwapInterval(x), pour que vous puissiez le changer sur un contexte existant.

Notez que la SDL 2.0 peut alterner entre modes plein écran et fenêtré avec les fenêtres OpenGL sans perdre le contexte OpenGL (Hourra !).Utilisez la fonction SDL_SetWindowFullscreen() pour cela.

II-D - Entrées

La bonne nouvelle est que la SDL 2.0 a rendu les entrées Unicode utilisables. La mauvaise nouvelle est que cela apporte des modifications mineures supplémentaires à votre code.

Avec la SDL 1.2, beaucoup d'applications qui ne se préoccupaient que de l'anglais américain continuaient d'appeler SDL_EnableUNICODE(1), car cela était pratique pour récupérer le caractère qui était associé à un appui de touche. Cela ne marchait pas très bien lorsque vous n'étiez pas anglais et cela ne marchait pas du tout avec les langues asiatiques.

Il s'avère que i18n est compliqué.

La SDL a changé et SDL_EnableUNICODE() a disparu ainsi que le champ de la structure unicode de la structure SDL_keysym. Vous ne recevez plus le caractère entré à partir de l'événement SDL_KEYDOWNSDL_KEYDOWN. Utilisez pour traiter le clavier comme un joystick à 101 boutons. Les insertions de texte arrivent autrement.

Le nouvel événement est SDL_TEXTINPUT. Il est déclenché à chaque fois qu'un nouveau texte est inséré par l'utilisateur. Notez que ce texte peut venir d'appuis sur les touches du clavier ou d'une quelconque méthode d'entrée (qui est une façon fantaisiste d'entrer du texte compliqué). Cet événement retourne des chaînes de caractères complètes, qui peuvent être composées d'un seul caractère ou de plusieurs morceaux de données multicaractères. La chaîne de caractères est toujours encodée en UTF-8.

Si vous vous souciez à propos des appuis d'un utilisateur sur une touche précise, il y a toujours SDL_KEYDOWN, mais nous avons séparé ce système en deux pièces depuis la version 1.2 : les codes de touche (« keycode ») et les codes de scan (« scancode »).

Les codes de scan sont conçus pour être indépendants du placement des touches. Imaginez cela comme « l'utilisateur a appuyé sur la touche Q comme cela aurait pu être sur le clavier QWERTY américain » sans se soucier si le clavier est européen ou Dvorak ou autre. Le code de scan correspond toujours à la même position de touche.

Les codes de touche sont conçus pour être dépendants du placement des touches. Imaginez cela comme « l'utilisateur a appuyé sur la touche marquée "Q" sur son clavier ».

Par exemple, si vous appuyez sur la touche qui est deux touches à droite du verrouillage de majuscules sur un clavier QWERTY américain, le code de scan renvoyé sera SDL_SCANCODE_S et le code de touche SDLK_S. La même touche sur un clavier Dvorak renvoie le code de scan SDL_SCANCODE_S et le code de touche SDLK_O.

Notez que les codes de touche et les codes de scan sont maintenant en 32 bits et utilisent un grand ensemble de nombres. Il n'existe plus de SDLK_LAST. Si votre programme possédait une table de correspondance de SDLK_LAST éléments pour faire correspondre les touches SDL à ce que votre application utilisait en interne, ce n'est plus faisable. Utilisez une table de hachage à la place. Une std::map fera l'affaire. Si vous faites correspondre les codes de scan à la place des codes de touche, il y a SDL_NUM_SCANCODES, que vous pouvez utiliser pour les limites du tableau. La constante correspond à 512 pour le moment.

SDLMod est maintenant SDL_Keymod et les touches « META » (la touche « Windows ») sont maintenant appelées les touches « GUI ».

La fonction SDL_GetKeyState() a été renommée en SDL_GetKeyboardState()SDL_SCANCODE_*. Le tableau renvoyé devrait être maintenant indexé par les valeurs SDL_SCANCODE_* (voir SDL_Scancode) à la place des valeurs de SDL_Keysym.

Passons aux entrées souris.

Le premier changement, assez simple, est que la roulette n'est plus un bouton. C'était une erreur historique et nous l'avons corrigée dans la SDL 2.0. Observez les événements SDL_MOUSEWHEELSDL_BUTTONDOWN. Nous supportons les roulettes horizontales et verticales et certaines plateformes peuvent traiter le défilement à deux doigts sur un trackpad aussi comme une entrée de roulette. Vous ne recevrez plus d'événements pour les roulettes de souris et les boutons 4 et 5 sont maintenant de vrais boutons de souris.

Si votre jeu nécessitait de faire rouler la souris dans une direction tout le temps, par exemple pour laisser un joueur dans un FPS tourner autour sans que la souris n'atteigne le bord de l'écran et ne s'arrête, vous avez certainement caché le curseur et capturé l'entrée :

SDL_ShowCursor(0);
SDL_WM_GrabInput(SDL_GRAB_ON);

Dans la SDL 2, cela fonctionne légèrement différemment. Vous appelez …

SDL_SetRelativeMouseMode(SDL_TRUE);

… et la SDL s'occupe du reste.

II-E - Événements

La fonction SDL_PushEvent() retourne maintenant 1 en cas de réussite, sinon 0.

Les masques d'événements sont maintenant spécifiés par ensemble :

SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_EVENTMASK(SDL_MOUSEBUTTONDOWN));

devient :

SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONDOWN);

II-F - Audio

La bonne nouvelle pour l'audio est que, à part une seule exception, la bibliothèque est entièrement rétrocompatible avec la version 1.2. Si vous souhaitez de nouvelles fonctionnalités, elles sont disponibles pour vous, mais vous pouvez juste compiler et exécuter votre projet sans elles.

Il y a une exception très importante : le callback audio NE démarre PLUS avec un tampon complètement initialisé. Vous devez entièrement écrire dans le tampon dans tous les cas. Si vous n'avez pas assez d'audio, votre callback devrait écrire du silence. Si vous échouez à cela, vous allez entendre un son répété ou peut-être une corruption audio. Si vous souhaitez restaurer l'ancien comportement de l'initialisation du tampon sans condition, placez juste un SDL_memset(steam, 0, len) au début du callback.

II-G - Joysticks

Les événements des joysticks se réfèrent maintenant à un SDL_JoystickID. Ceci est dû au fait que la SDL 2.0 peut gérer les joysticks, se connecter et se déconnecter, suivant les connexions des périphériques durant la vie du jeu, donc l'index de la liste des périphériques qu'utilise la SDL 1.2 n'aurait aucun sens comme la liste des périphériques change.

Pour obtenir un SDL_JoystickID pour votre SDL_Joystick* ouvert, appelez :

SDL_JoystickID myID = SDL_JoystickInstanceID(myOpenedStick);

Et comparez le champ which des événements du joystick avec myID. Si vous n'utilisez pas la queue des événements pour les joysticks, SDL_JoystickGetAxis() et autres s'utilisent exactement comme dans la version 1.2.

Vous devriez aussi voir la nouvelle bibliothèque de contrôleur de jeu, car elle est sympa et peut-être que vous avez effectué un numéro de danse avec la version 1.2 que ce nouveau code résoudra plus proprement. Vous pouvez la trouver dans SDL_gamecontroller.h. La bibliothèque de contrôleur de jeu s'intègre très bien avec le mode Steam Big Picture : vous obtenez une configuration automatique pour la majorité des contrôleurs et une interface sympa si vous devez la configurer manuellement. Dans les deux cas, Steam passe cette configuration à votre application SDL.

Le support pour l'ancienne bibliothèque de joysticks (/dev/input/js*) pour Linux a été abandonné pour la SDL 2. La SDL 2 supporte uniquement les récentes bibliothèques événementielles (/dev/input/event/*) pour les joysticks. Ces événements ne sont normalement pas lisibles pour les utilisateurs normaux, donc même si des joysticks sont connectés vous pouvez n'en détecter aucun. C'est quelque chose que les utilisateurs finals doivent configurer eux-mêmes.

II-H - Threads

La fonction SDL_KillThread() a été retirée. Elle n'était pas sécurisée ni même fiable. Le meilleur remplacement est de définir un indicateur qui indique si le thread doit s'arrêter. Ce thread doit vérifier régulièrement cet indicateur et le thread « tueur » appelle SDL_WaitThread() pour libérer les ressources.

La fonction SDL_CreateThread() prend maintenant un paramètre supplémentaire, un nom pour le thread, qui peut être utilisé par les débogueurs pour l'identifier. Si vous ne vous en souciez pas, rajoutez simplement NULL à l'appel de fonction.

II-I - CD Audio

La bibliothèque des CD est complètement partie. Il n'y a pas de remplacement. De toute façon, vous ne publiez plus votre musique en piste audio sur un CD, si vous publiez encore en utilisant des CD. Vous pouvez utiliser Ogg Vorbis ou un autre format de fichier audio pour la musique, dont certains sont fournis par SDL_Mixer.

II-J - Plateformes mortes

Nous avons supprimé de nombreuses vieilles plateformes, comme OS/2 et Mac OS 9. Il sera plus facile de lister les plateformes que nous continuons de supporter : Windows (XP et supérieur), Linux, Mac OS X, iOS, Android. Dans la tradition de la SDL, il y a d'autres plateformes qui fonctionnent, mais qui ne sont pas intensivement supportées, comme Haiku et la PSP. Nous allons ajouter les plateformes pour lesquelles quelqu'un enverra un patch, mais il semble qu'il était temps de dire au revoir à nos vieux amis en passant à cette nouvelle version.

II-K - Plateformes mobiles

Il y avait, depuis plusieurs années, des portages non officiels de la SDL 1.2 pour iOS et Android. La SDL supporte maintenant ces plateformes directement et la bibliothèque 2.0 est mieux conçue pour celles-ci. La plupart des conseils que vous avez lus dans ce document fonctionnent, mais il y a quelques autres points à noter.

Premièrement, il y a quelques événements qui ne s'appliquent qu'aux périphériques mobiles, ou dit autrement, s'appliquent de la façon dont les systèmes sur mobiles essaient d'opérer dans un monde post iPhone. Au début, nous avons essayé de faire correspondre ces événements aux événements existants de la SDL (comme l'événement « votre application va être mise en arrière-plan » traité comme une perte de focus pour une fenêtre de bureau), mais il y a des problèmes plus urgents : la majorité de ces événements nécessitent une réponse immédiate et si l'application n'en donne pas, le système la tuera.

Ainsi, nous avons ajouté de nouveaux événements dans la SDL pour certains détails spécifiques à Android et iOS, mais vous devez définir un filtre d'événements SDL pour les capturer dès que le système les rapporte sinon l'attente pour le prochain SDL_PollEvent() sera trop longue.

Par exemple, il y a SDL_APP_WILLENTERBACKGROUND, qui correspond à la fonction iOS applicationWillResignActive() et si vous dessinez à l'écran après l'arrivée de cet événement, iOS termine votre processus. Donc, vous souhaitez le capturer immédiatement :

int SDLCALL myEventFilter(void *userdata, SDL_Event * event)
{
if (event->type == SDL_APP_WILLENTERBACKGROUND) {
// libération des ressources, ne dessinez plus tant que vous ne revenez pas au premier plan !
}
// etc
return 1;
}

// quelque part proche du démarrage...

// cela appel myEventFilter(data, event) aussi tôt que l'événement est généré.
SDL_AddEventWatch(myEventFilter, data);

Deuxièmement, il y a maintenant de vrais événements pour le toucher à la place d'essayer de faire correspondre ceux-ci à la souris. Vous pouvez suivre le toucher, le multi-touch et même des gestes plus complexes. Vous souhaitez probablement les utiliser. Référez-vous à SDL_Touch.h pour la liste de ces fonctions et regardez pour SDL_FINGER* dans SDL_events.h.

Il y a d'autres fonctions pour les mobiles, comme SDL_StartTextInput() qui affichera un clavier sur l'écran. Servez-vous de celles-ci.

De plus, il y a aussi des fonctions spécifiques à Android et iOS pour vous laisser accéder aux fonctionnalités spécifiques à ces plateformes et qui n'auraient aucun sens d'être dans la bibliothèque principale. Référez-vous au fichier SDL_system.h pour une liste de ces fonctions.

II-L - RWops

SDL_RWread() et SDL_RWwrite() retournent maintenant 0 en cas d'erreur et non plus -1.

Si vous créez votre propre implémentation de SDL_RWops, les signatures de fonctions ont changé. Maintenant, les fonctions utilisent Sint64 et size_t à la place de int et donc peuvent travailler sur des fichiers plus grands. Dans la plupart des cas, vous pouvez simplement mettre à jour vos signatures de fonctions et continuer de travailler comme avant, mais si vous aviez rencontré ces limitations, vous serez content d'avoir une solution. Les applications appelantes devraient prendre en compte ce changement.

Il y a aussi une méthode size dans les RWops. Cela permet aux RWops de retourner la taille du flux sans que l'application nécessite de chercher l'octet à zéro à la fin ; en d'autres termes, vous pouvez retourner la taille totale pour les flux qui ne peuvent pas être parcourus. Pour les flux qui ne peuvent même pas faire cela, vous pouvez retourner -1.

II-M - Bibliothèques supplémentaires

Les extensions officielles SDL_image, SDL_ttf, SDL_mixer et SDL_net possèdent une version dédiée à la SDL 2.0 : SDL2_image, SDL2_ttf, SDL2_mixer et SDL2_net. Vous pouvez les télécharger à partir du dépôt mercurial pour les dernières corrections. Ensuite, bien sûr, vous devez lier, par exemple SDL2_image à la place de SDL_image pour compiler votre programme.

Ces bibliothèques ne vont plus supporter la version 1.2 et les compatibilités avec la version 1.2 vont disparaître au bout d'un moment dans les futures versions.

SDL_gfx peut aussi être compilée avec la version 2.0 à partir de la version 2.0.21 (mai 2010).

II-N - Sommaire des choses renommées ou remplacées

Une courte feuille de triche des fonctions des remplacements des fonctions et autres :

- SDL_SetVideoMode() : utilisez SDL_CreateWindow() à la place (avec la fonction SDL_CreateRenderer() si vous souhaitez un rendu 2D classique et non pas OpenGL) ;

- SDL_ListModes() : utilisez SDL_GetDisplayMode()/SDL_GetNumDisplayModes() à la place ;

- SDL_UpdateRect()/SDL_Flip() : utilisez SDL_RenderPresent() à la place ;

- SDL_Surface/rendu 2D : les surfaces existent toujours, mais il est recommandé d'utiliser les SDL_Texture à la place, avec un moteur de rendus 2D accéléré (SDL_CreateRenderer()) là où cela est possible ;

- SDL_VideoInfo : utilisez SDL_GetRendererInfo()/SDL_GetRenderDriverInfo() à la place ;

- SDL_GetCurrentVideoDisplay() : utilisez SDL_GetWindowDisplayIndex() à la place ;

- événement SDL_VIDEORESIZE : la nouvelle équivalence est SDL_WINDOWRESIZE_EVENT.

II-O - Autres choses

Il y a énormément de nouvelles et intéressantes fonctionnalités dans la SDL 2.0 dont la SDL 1.2 ne pouvait pas rêver. Nous avons seulement essayé de vous expliquer ici ce que vous pouvez avoir à faire pour obtenir votre programme SDL 1.2 fonctionnel avec la SDL 2.0, mais vous devez explorer la documentation pour les choses que vous avez toujours souhaitées et, jusqu'à présent, fait sans. Par exemple, tous les jeux que j'ai portés finissaient avec une fonction de popup qui ressemblait à celle-ci :

#if USING_SDL
fprintf(stderr, "MSGBOX: %s\n%s\n", title, text);   // oh soit.
#endif

Maintenant, il y a SDL_ShowSimpleMessageBox(). Vous êtes les bienvenus.

Si vous ne l'aviez pas remarqué, retournez en arrière et lisez la liste des nouvelles fonctionnalités.

III - Remerciements

Merci à Sam Lantiga et Ryan Gordon de nous avoir permis de traduire cette documentation. Vous pouvez trouver le document original sur le wiki officiel.

Merci à Torgar et ClaudeLELOUP pour leur correction orthographique.