Homebrew Question SDL2 Switch Development Woes

delete12345

Well-Known Member
OP
Member
Joined
Feb 27, 2010
Messages
695
Trophies
1
Age
32
Location
Taipei, Taiwan
XP
1,276
Country
United States
This thread will document the many challenges and difficulties in developing homebrew for the Nintendo Switch, using SDL2 library. Below is the table of contents of breakthroughs, small and large, that will redirect you to the relevant posts in this thread:

Table of Contents:

More posts will be added to the table of contents along the way when new findings in regards to making better SDL2 codes are posted.

Please feel free to submit your own SDL2 breakthroughs to this thread, so everyone else can learn and share.
 
Last edited by delete12345,

delete12345

Well-Known Member
OP
Member
Joined
Feb 27, 2010
Messages
695
Trophies
1
Age
32
Location
Taipei, Taiwan
XP
1,276
Country
United States
Original Post:

Currently trying to figure out how to work with SDL2 at the moment. The SDL2 I'm using is from devkitpro, with the SDL2 fork for the Switch.

Right now, the code will cause the program to crash on the Switch. And I'm trying to troubleshoot this.

Code:
#include <switch.h>

#include <SDL.h>
#include <SDL_ttf.h>

int main(int argc, char* argv[]) {
    SDL_Init(SDL_INIT_EVERYTHING);

    /*gfxInitDefault();
    consoleInit(nullptr);*/
   
    //Switch screen size: 720p. Must set to full screen.
    SDL_Window* window = SDL_CreateWindow(nullptr, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_FULLSCREEN);
    if (!window)
        SDL_Quit();
    SDL_Renderer* renderer = SDL_CreateRenderer(window, 0, SDL_RENDERER_SOFTWARE);
    if (!renderer)
        SDL_Quit();
    SDL_Surface* windowSurface = SDL_GetWindowSurface(window);

    while (appletMainLoop()) {
        hidScanInput();

        u32 keyDown = hidKeysDown(CONTROLLER_P1_AUTO);
        if (keyDown & KEY_PLUS)
            break;

        SDL_RenderClear(renderer);

        /*gfxFlushBuffers();
        gfxSwapBuffers();
        gfxWaitForVsync();*/

        SDL_Delay(1);
    }

    SDL_FreeSurface(windowSurface);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);

    SDL_Quit();

    //gfxExit();
    return 0;
}

The commented code may be up for debate, because who knows at this point if the buffer swapping is done automatically by SDL2, or that I need to uncomment them. The crash is caused when it's attempting to initialize a surface, and then attempting to quit out of the application. But somehow, it's stuck. I don't know the cause, because from a glance, this code is just merely refreshing the screen to black, and polls whether the Plus button on the right Joycon is pressed or not. It shouldn't be getting stuck.

If anyone knows how to troubleshoot this, I'll appreciate your help.


Second Post:


At least I can now see some output log messages. But I don't understand why it's not picking up Joycon buttons at the moment.

The output message says:

Hello world
SDL EVENT: SDL_AUDIODEVICEADDED

And then, no more SDL_Event for inputs from the Joycons.

Does anyone know how to obtain the inputs from the Joycons using SDL2?

Code:
#include <switch.h>
#include <stdio.h>

#include <SDL2/SDL.h>
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_ttf.h>

//Copied / pasted from the SDL source.

static void SDL_DebugPrintEvent(const SDL_Event *event)
{
    printf("SDL EVENT: ");

    if ((event->type >= SDL_USEREVENT) && (event->type <= SDL_LASTEVENT)) {
        printf("SDL_USEREVENT");
        if (event->type > SDL_USEREVENT) {
            printf("+%u", ((uint) event->type) - SDL_USEREVENT);
        }
        printf(" (timestamp=%u windowid=%u code=%d data1=%p data2=%p)",
            (uint) event->user.timestamp, (uint) event->user.windowID,
            (int) event->user.code, event->user.data1, event->user.data2);
        return;
    }

    switch (event->type) {
#define SDL_EVENT_CASE(x) case x: printf("%s", #x);
        SDL_EVENT_CASE(SDL_FIRSTEVENT) printf("(THIS IS PROBABLY A BUG!)"); break;
        SDL_EVENT_CASE(SDL_QUIT) printf("(timestamp=%u)", (uint) event->quit.timestamp); break;
        SDL_EVENT_CASE(SDL_APP_TERMINATING) break;
        SDL_EVENT_CASE(SDL_APP_LOWMEMORY) break;
        SDL_EVENT_CASE(SDL_APP_WILLENTERBACKGROUND) break;
        SDL_EVENT_CASE(SDL_APP_DIDENTERBACKGROUND) break;
        SDL_EVENT_CASE(SDL_APP_WILLENTERFOREGROUND) break;
        SDL_EVENT_CASE(SDL_APP_DIDENTERFOREGROUND) break;
        SDL_EVENT_CASE(SDL_KEYMAPCHANGED) break;
        SDL_EVENT_CASE(SDL_CLIPBOARDUPDATE) break;
        SDL_EVENT_CASE(SDL_RENDER_TARGETS_RESET) break;
        SDL_EVENT_CASE(SDL_RENDER_DEVICE_RESET) break;
#undef SDL_EVENT_CASE

#define SDL_EVENT_CASE(x) case x: printf("%s ", #x);

        SDL_EVENT_CASE(SDL_WINDOWEVENT)
            printf("(timestamp=%u windowid=%u event=", (uint) event->window.timestamp, (uint) event->window.windowID);
        switch (event->window.event) {
            case SDL_WINDOWEVENT_NONE: printf("none(THIS IS PROBABLY A BUG!)"); break;
#define SDL_WINDOWEVENT_CASE(x) case x: printf("%s", #x); break
                SDL_WINDOWEVENT_CASE(SDL_WINDOWEVENT_SHOWN);
                SDL_WINDOWEVENT_CASE(SDL_WINDOWEVENT_HIDDEN);
                SDL_WINDOWEVENT_CASE(SDL_WINDOWEVENT_EXPOSED);
                SDL_WINDOWEVENT_CASE(SDL_WINDOWEVENT_MOVED);
                SDL_WINDOWEVENT_CASE(SDL_WINDOWEVENT_RESIZED);
                SDL_WINDOWEVENT_CASE(SDL_WINDOWEVENT_SIZE_CHANGED);
                SDL_WINDOWEVENT_CASE(SDL_WINDOWEVENT_MINIMIZED);
                SDL_WINDOWEVENT_CASE(SDL_WINDOWEVENT_MAXIMIZED);
                SDL_WINDOWEVENT_CASE(SDL_WINDOWEVENT_RESTORED);
                SDL_WINDOWEVENT_CASE(SDL_WINDOWEVENT_ENTER);
                SDL_WINDOWEVENT_CASE(SDL_WINDOWEVENT_LEAVE);
                SDL_WINDOWEVENT_CASE(SDL_WINDOWEVENT_FOCUS_GAINED);
                SDL_WINDOWEVENT_CASE(SDL_WINDOWEVENT_FOCUS_LOST);
                SDL_WINDOWEVENT_CASE(SDL_WINDOWEVENT_CLOSE);
                SDL_WINDOWEVENT_CASE(SDL_WINDOWEVENT_TAKE_FOCUS);
                SDL_WINDOWEVENT_CASE(SDL_WINDOWEVENT_HIT_TEST);
#undef SDL_WINDOWEVENT_CASE
            default: printf("UNKNOWN(bug? fixme?)"); break;
        }
        printf(" data1=%d data2=%d)", (int) event->window.data1, (int) event->window.data2);
        break;

        SDL_EVENT_CASE(SDL_SYSWMEVENT)
            printf("(timestamp=%u)", (uint) event->syswm.timestamp);
        /* !!! FIXME: we don't delve further at the moment. */
        break;

#define PRINT_KEY_EVENT(event) \
            printf("(timestamp=%u windowid=%u state=%s repeat=%s scancode=%u keycode=%u mod=%u)", \
                (uint) event->key.timestamp, (uint) event->key.windowID, \
                event->key.state == SDL_PRESSED ? "pressed" : "released", \
                event->key.repeat ? "true" : "false", \
                (uint) event->key.keysym.scancode, \
                (uint) event->key.keysym.sym, \
                (uint) event->key.keysym.mod)
        SDL_EVENT_CASE(SDL_KEYDOWN) PRINT_KEY_EVENT(event); break;
        SDL_EVENT_CASE(SDL_KEYUP) PRINT_KEY_EVENT(event); break;
#undef PRINT_KEY_EVENT

        SDL_EVENT_CASE(SDL_TEXTEDITING)
            printf("(timestamp=%u windowid=%u text='%s' start=%d length=%d)",
            (uint) event->edit.timestamp, (uint) event->edit.windowID,
                event->edit.text, (int) event->edit.start, (int) event->edit.length);
        break;

        SDL_EVENT_CASE(SDL_TEXTINPUT)
            printf("(timestamp=%u windowid=%u text='%s')", (uint) event->text.timestamp, (uint) event->text.windowID, event->text.text);
        break;


        SDL_EVENT_CASE(SDL_MOUSEMOTION)
            printf("(timestamp=%u windowid=%u which=%u state=%u x=%d y=%d xrel=%d yrel=%d)",
            (uint) event->motion.timestamp, (uint) event->motion.windowID,
                (uint) event->motion.which, (uint) event->motion.state,
                (int) event->motion.x, (int) event->motion.y,
                (int) event->motion.xrel, (int) event->motion.yrel);
        break;

#define PRINT_MBUTTON_EVENT(event) \
            printf("(timestamp=%u windowid=%u which=%u button=%u state=%s clicks=%u x=%d y=%d)", \
                    (uint) event->button.timestamp, (uint) event->button.windowID, \
                    (uint) event->button.which, (uint) event->button.button, \
                    event->button.state == SDL_PRESSED ? "pressed" : "released", \
                    (uint) event->button.clicks, (int) event->button.x, (int) event->button.y)
        SDL_EVENT_CASE(SDL_MOUSEBUTTONDOWN) PRINT_MBUTTON_EVENT(event); break;
        SDL_EVENT_CASE(SDL_MOUSEBUTTONUP) PRINT_MBUTTON_EVENT(event); break;
#undef PRINT_MBUTTON_EVENT


        SDL_EVENT_CASE(SDL_MOUSEWHEEL)
            printf("(timestamp=%u windowid=%u which=%u x=%d y=%d direction=%s)",
            (uint) event->wheel.timestamp, (uint) event->wheel.windowID,
                (uint) event->wheel.which, (int) event->wheel.x, (int) event->wheel.y,
                event->wheel.direction == SDL_MOUSEWHEEL_NORMAL ? "normal" : "flipped");
        break;

        SDL_EVENT_CASE(SDL_JOYAXISMOTION)
            printf("(timestamp=%u which=%d axis=%u value=%d)",
            (uint) event->jaxis.timestamp, (int) event->jaxis.which,
                (uint) event->jaxis.axis, (int) event->jaxis.value);
        break;

        SDL_EVENT_CASE(SDL_JOYBALLMOTION)
            printf("(timestamp=%u which=%d ball=%u xrel=%d yrel=%d)",
            (uint) event->jball.timestamp, (int) event->jball.which,
                (uint) event->jball.ball, (int) event->jball.xrel, (int) event->jball.yrel);
        break;

        SDL_EVENT_CASE(SDL_JOYHATMOTION)
            printf("(timestamp=%u which=%d hat=%u value=%u)",
            (uint) event->jhat.timestamp, (int) event->jhat.which,
                (uint) event->jhat.hat, (uint) event->jhat.value);
        break;

#define PRINT_JBUTTON_EVENT(event) \
            printf("(timestamp=%u which=%d button=%u state=%s)", \
                (uint) event->jbutton.timestamp, (int) event->jbutton.which, \
                (uint) event->jbutton.button, event->jbutton.state == SDL_PRESSED ? "pressed" : "released")
        SDL_EVENT_CASE(SDL_JOYBUTTONDOWN) PRINT_JBUTTON_EVENT(event); break;
        SDL_EVENT_CASE(SDL_JOYBUTTONUP) PRINT_JBUTTON_EVENT(event); break;
#undef PRINT_JBUTTON_EVENT

#define PRINT_JOYDEV_EVENT(event) printf("(timestamp=%u which=%d)", (uint) event->jdevice.timestamp, (int) event->jdevice.which)
        SDL_EVENT_CASE(SDL_JOYDEVICEADDED) PRINT_JOYDEV_EVENT(event); break;
        SDL_EVENT_CASE(SDL_JOYDEVICEREMOVED) PRINT_JOYDEV_EVENT(event); break;
#undef PRINT_JOYDEV_EVENT

        SDL_EVENT_CASE(SDL_CONTROLLERAXISMOTION)
            printf("(timestamp=%u which=%d axis=%u value=%d)",
            (uint) event->caxis.timestamp, (int) event->caxis.which,
                (uint) event->caxis.axis, (int) event->caxis.value);
        break;

#define PRINT_CBUTTON_EVENT(event) \
            printf("(timestamp=%u which=%d button=%u state=%s)", \
                (uint) event->cbutton.timestamp, (int) event->cbutton.which, \
                (uint) event->cbutton.button, event->cbutton.state == SDL_PRESSED ? "pressed" : "released")
        SDL_EVENT_CASE(SDL_CONTROLLERBUTTONDOWN) PRINT_CBUTTON_EVENT(event); break;
        SDL_EVENT_CASE(SDL_CONTROLLERBUTTONUP) PRINT_CBUTTON_EVENT(event); break;
#undef PRINT_CBUTTON_EVENT

#define PRINT_CONTROLLERDEV_EVENT(event) printf("(timestamp=%u which=%d)", (uint) event->cdevice.timestamp, (int) event->cdevice.which)
        SDL_EVENT_CASE(SDL_CONTROLLERDEVICEADDED) PRINT_CONTROLLERDEV_EVENT(event); break;
        SDL_EVENT_CASE(SDL_CONTROLLERDEVICEREMOVED) PRINT_CONTROLLERDEV_EVENT(event); break;
        SDL_EVENT_CASE(SDL_CONTROLLERDEVICEREMAPPED) PRINT_CONTROLLERDEV_EVENT(event); break;
#undef PRINT_CONTROLLERDEV_EVENT

#define PRINT_FINGER_EVENT(event) \
            printf("(timestamp=%u touchid=%lld fingerid=%lld x=%f y=%f dx=%f dy=%f pressure=%f)", \
                (uint) event->tfinger.timestamp, (long long) event->tfinger.touchId, \
                (long long) event->tfinger.fingerId, event->tfinger.x, event->tfinger.y, \
                event->tfinger.dx, event->tfinger.dy, event->tfinger.pressure)
        SDL_EVENT_CASE(SDL_FINGERDOWN) PRINT_FINGER_EVENT(event); break;
        SDL_EVENT_CASE(SDL_FINGERUP) PRINT_FINGER_EVENT(event); break;
        SDL_EVENT_CASE(SDL_FINGERMOTION) PRINT_FINGER_EVENT(event); break;
#undef PRINT_FINGER_EVENT

#define PRINT_DOLLAR_EVENT(event) \
            printf("(timestamp=%u touchid=%lld gestureid=%lld numfingers=%u error=%f x=%f y=%f)", \
                (uint) event->dgesture.timestamp, (long long) event->dgesture.touchId, \
                (long long) event->dgesture.gestureId, (uint) event->dgesture.numFingers, \
                event->dgesture.error, event->dgesture.x, event->dgesture.y);
        SDL_EVENT_CASE(SDL_DOLLARGESTURE) PRINT_DOLLAR_EVENT(event); break;
        SDL_EVENT_CASE(SDL_DOLLARRECORD) PRINT_DOLLAR_EVENT(event); break;
#undef PRINT_DOLLAR_EVENT

        SDL_EVENT_CASE(SDL_MULTIGESTURE)
            printf("(timestamp=%u touchid=%lld dtheta=%f ddist=%f x=%f y=%f numfingers=%u)",
            (uint) event->mgesture.timestamp, (long long) event->mgesture.touchId,
                event->mgesture.dTheta, event->mgesture.dDist,
                event->mgesture.x, event->mgesture.y, (uint) event->mgesture.numFingers);
        break;

#define PRINT_DROP_EVENT(event) printf("(file='%s' timestamp=%u windowid=%u)", event->drop.file, (uint) event->drop.timestamp, (uint) event->drop.windowID)
        SDL_EVENT_CASE(SDL_DROPFILE) PRINT_DROP_EVENT(event); break;
        SDL_EVENT_CASE(SDL_DROPTEXT) PRINT_DROP_EVENT(event); break;
        SDL_EVENT_CASE(SDL_DROPBEGIN) PRINT_DROP_EVENT(event); break;
        SDL_EVENT_CASE(SDL_DROPCOMPLETE) PRINT_DROP_EVENT(event); break;
#undef PRINT_DROP_EVENT

#define PRINT_AUDIODEV_EVENT(event) printf("(timestamp=%u which=%u iscapture=%s)", (uint) event->adevice.timestamp, (uint) event->adevice.which, event->adevice.iscapture ? "true" : "false");
        SDL_EVENT_CASE(SDL_AUDIODEVICEADDED) PRINT_AUDIODEV_EVENT(event); break;
        SDL_EVENT_CASE(SDL_AUDIODEVICEREMOVED) PRINT_AUDIODEV_EVENT(event); break;
#undef PRINT_AUDIODEV_EVENT

#undef SDL_EVENT_CASE

        default:
            printf("UNKNOWN SDL EVENT #%u! (Bug? FIXME?)", (uint) event->type);
            break;
    }

    printf("\n");
}


//Actual code

int main(int argc, char* argv[]) {
    gfxInitDefault();
    consoleInit(nullptr);

    SDL_Init(SDL_INIT_EVERYTHING);

    printf("Hello world");

    while (appletMainLoop()) {
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            SDL_DebugPrintEvent(&event);
        }
    }

    SDL_Quit();

    gfxExit();
    return 0;
}
 
Last edited by delete12345,

delete12345

Well-Known Member
OP
Member
Joined
Feb 27, 2010
Messages
695
Trophies
1
Age
32
Location
Taipei, Taiwan
XP
1,276
Country
United States
I think you should double check you code, that if statement mainly...
No worries, since that long if statement is copied directly from the SDL codebase.

I figured out why the Switch keeps crashing.

I should not be mixing <switch.h> related API functions and SDL2 functions together. Particularly, when initializing and setting up the video memory.

gfxInitDefault() and consoleInit(NULL) both initializes the video memory, which SDL_InitSubSystem(SDL_INIT_VIDEO) would also overwrite the initializations, so these 2 things (switch.h and SDL2) conflict with each other.

With that in mind, after commenting out gfxExit(), program now works.

So, the main points to drive home is: Always use SDL2 only if you use at least 1 SDL2 API function. Always use <switch.h> only if you use at least 1 of the libnx functions.

Working code. Now I need to figure out the inputs and joycon buttons.

Code:
#include <switch.h>
#include <iostream>

#include <SDL2/SDL.h>
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_ttf.h>

int main(int argc, char* argv[]) {
    //Do not use anything from <switch.h>
    //gfxInitDefault();
    //consoleInit(nullptr);

    SDL_Init(SDL_INIT_EVERYTHING);
   
    //Switch screen size: 720p. Must set to full screen.
    SDL_Window* window = SDL_CreateWindow(nullptr, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 0, 0, SDL_WINDOW_FULLSCREEN_DESKTOP);
    if (!window)
        SDL_Quit();
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE);
    if (!renderer)
        SDL_Quit();
    SDL_Surface* screen = SDL_GetWindowSurface(window);
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, screen);

    uint32_t* pixels = (uint32_t*) screen->pixels;

    SDL_SetRenderDrawColor(renderer, 33, 33, 128, 255);
    SDL_RenderClear(renderer);
    SDL_RenderPresent(renderer);

    while (appletMainLoop()) {
        bool flag = false;
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            switch (event.type) {
                case SDL_KEYDOWN:
                    u32 key = event.key.keysym.sym;
                    u32 mod = event.key.keysym.mod;
                    std::cout << "Input: " << key << " " << mod << std::endl;
                    flag = true;
                    break;
            }
        }
        if (flag)
            break;

        pixels[23 * screen->pitch + 40] = 0x12345678;

        SDL_UpdateTexture(texture, nullptr, screen->pixels, screen->pitch);

        SDL_RenderClear(renderer);
        SDL_RenderCopy(renderer, texture, nullptr, nullptr);
        SDL_RenderPresent(renderer);
    }

    SDL_DestroyTexture(texture);
    SDL_FreeSurface(screen);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);

    //No libnx function calls.
    //gfxExit();

    SDL_Quit();
    return 0;
}
 
Last edited by delete12345,

cpasjuste

Well-Known Member
Member
Joined
Aug 27, 2015
Messages
1,108
Trophies
1
Age
44
XP
4,481
Country
France
No worries, since that long if statement is copied directly from the SDL codebase.

I figured out why the Switch keeps crashing.

I should not be mixing <switch.h> related API functions and SDL2 functions together. Particularly, when initializing and setting up the video memory.

gfxInitDefault() and consoleInit(NULL) both initializes the video memory, which SDL_InitSubSystem(SDL_INIT_VIDEO) would also overwrite the initializations, so these 2 things (switch.h and SDL2) conflict with each other.

With that in mind, after commenting out gfxExit(), program now works.

So, the main points to drive home is: Always use SDL2 only if you use at least 1 SDL2 API function. Always use <switch.h> only if you use at least 1 of the libnx functions.

Working code. Now I need to figure out the inputs and joycon buttons.

Code:
#include <switch.h>
#include <iostream>

#include <SDL2/SDL.h>
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_ttf.h>

int main(int argc, char* argv[]) {
    //Do not use anything from <switch.h>
    //gfxInitDefault();
    //consoleInit(nullptr);

    SDL_Init(SDL_INIT_EVERYTHING);
 
    //Switch screen size: 720p. Must set to full screen.
    SDL_Window* window = SDL_CreateWindow(nullptr, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 0, 0, SDL_WINDOW_FULLSCREEN_DESKTOP);
    if (!window)
        SDL_Quit();
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE);
    if (!renderer)
        SDL_Quit();
    SDL_Surface* screen = SDL_GetWindowSurface(window);
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, screen);

    uint32_t* pixels = (uint32_t*) screen->pixels;

    SDL_SetRenderDrawColor(renderer, 33, 33, 128, 255);
    SDL_RenderClear(renderer);
    SDL_RenderPresent(renderer);

    while (appletMainLoop()) {
        bool flag = false;
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            switch (event.type) {
                case SDL_KEYDOWN:
                    u32 key = event.key.keysym.sym;
                    u32 mod = event.key.keysym.mod;
                    std::cout << "Input: " << key << " " << mod << std::endl;
                    flag = true;
                    break;
            }
        }
        if (flag)
            break;

        pixels[23 * screen->pitch + 40] = 0x12345678;

        SDL_UpdateTexture(texture, nullptr, screen->pixels, screen->pitch);

        SDL_RenderClear(renderer);
        SDL_RenderCopy(renderer, texture, nullptr, nullptr);
        SDL_RenderPresent(renderer);
    }

    SDL_DestroyTexture(texture);
    SDL_FreeSurface(screen);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);

    //No libnx function calls.
    //gfxExit();

    SDL_Quit();
    return 0;
}

Hi, you can deduct buttons from here : https://github.com/devkitPro/SDL/blob/switch-sdl2/src/joystick/switch/SDL_sysjoystick.c#L52 :
- A = SDL button 0, B = 1, X = 2 .... You need to look for SDL joystick event too, not key (keyboard) events. You can find a sample here : https://github.com/devkitPro/SDL/blob/switch-sdl2/test/testswitch.c

Also, you can/may use "switch.h", just don't use any (switch) gfx code.
 
Last edited by cpasjuste,

delete12345

Well-Known Member
OP
Member
Joined
Feb 27, 2010
Messages
695
Trophies
1
Age
32
Location
Taipei, Taiwan
XP
1,276
Country
United States
Hi, you can deduct buttons from here : https://github.com/devkitPro/SDL/blob/switch-sdl2/src/joystick/switch/SDL_sysjoystick.c#L52 :
- A = SDL button 0, B = 1, X = 2 .... You need to look for SDL joystick event too, not key (keyboard) events. You can find a sample here : https://github.com/devkitPro/SDL/blob/switch-sdl2/test/testswitch.c

Also, you can/may use "switch.h", just don't use any (switch) gfx code.
Thank you. I was looking around the devkitpro SDL repository, and I was not able to find information in regards to inputs, video memory management, and how to port an SDL2 build to the Switch.

Hopefully, there are more test codes coming.
 

cpasjuste

Well-Known Member
Member
Joined
Aug 27, 2015
Messages
1,108
Trophies
1
Age
44
XP
4,481
Country
France
Thank you. I was looking around the devkitpro SDL repository, and I was not able to find information in regards to inputs, video memory management, and how to port an SDL2 build to the Switch.

Hopefully, there are more test codes coming.
There's no more test code to come, you can follow any SDL2 tutorial :P
 

delete12345

Well-Known Member
OP
Member
Joined
Feb 27, 2010
Messages
695
Trophies
1
Age
32
Location
Taipei, Taiwan
XP
1,276
Country
United States
When using SDL2, what's the best method to print a "Hello world" sentence?

Since, you can't use consoleInit() and gfxInitDefault() to set up the font rendering and the console output, I'm stuck wondering about this, and how to go about it.


UPDATE:

Found it! Somehow, the debugDevice_SVC is important.

consoleDebugInit(debugDevice_SVC);

And then I can choose whether to do this or not:

stdout = stderr;

It's optional.
 
Last edited by delete12345,

laramie

Well-Known Member
Member
Joined
Dec 15, 2014
Messages
911
Trophies
0
XP
1,387
Country
United States
No worries, since that long if statement is copied directly from the SDL codebase.

I figured out why the Switch keeps crashing.

I should not be mixing <switch.h> related API functions and SDL2 functions together. Particularly, when initializing and setting up the video memory.

gfxInitDefault() and consoleInit(NULL) both initializes the video memory, which SDL_InitSubSystem(SDL_INIT_VIDEO) would also overwrite the initializations, so these 2 things (switch.h and SDL2) conflict with each other.

With that in mind, after commenting out gfxExit(), program now works.

So, the main points to drive home is: Always use SDL2 only if you use at least 1 SDL2 API function. Always use <switch.h> only if you use at least 1 of the libnx functions.

Working code. Now I need to figure out the inputs and joycon buttons.

Code:
#include <switch.h>
#include <iostream>

#include <SDL2/SDL.h>
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_ttf.h>

int main(int argc, char* argv[]) {
    //Do not use anything from <switch.h>
    //gfxInitDefault();
    //consoleInit(nullptr);

    SDL_Init(SDL_INIT_EVERYTHING);
  
    //Switch screen size: 720p. Must set to full screen.
    SDL_Window* window = SDL_CreateWindow(nullptr, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 0, 0, SDL_WINDOW_FULLSCREEN_DESKTOP);
    if (!window)
        SDL_Quit();
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE);
    if (!renderer)
        SDL_Quit();
    SDL_Surface* screen = SDL_GetWindowSurface(window);
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, screen);

    uint32_t* pixels = (uint32_t*) screen->pixels;

    SDL_SetRenderDrawColor(renderer, 33, 33, 128, 255);
    SDL_RenderClear(renderer);
    SDL_RenderPresent(renderer);

    while (appletMainLoop()) {
        bool flag = false;
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            switch (event.type) {
                case SDL_KEYDOWN:
                    u32 key = event.key.keysym.sym;
                    u32 mod = event.key.keysym.mod;
                    std::cout << "Input: " << key << " " << mod << std::endl;
                    flag = true;
                    break;
            }
        }
        if (flag)
            break;

        pixels[23 * screen->pitch + 40] = 0x12345678;

        SDL_UpdateTexture(texture, nullptr, screen->pixels, screen->pitch);

        SDL_RenderClear(renderer);
        SDL_RenderCopy(renderer, texture, nullptr, nullptr);
        SDL_RenderPresent(renderer);
    }

    SDL_DestroyTexture(texture);
    SDL_FreeSurface(screen);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);

    //No libnx function calls.
    //gfxExit();

    SDL_Quit();
    return 0;
}


Yeah I figured, well good luck with whateverr it is you are doing, cheers!
 

delete12345

Well-Known Member
OP
Member
Joined
Feb 27, 2010
Messages
695
Trophies
1
Age
32
Location
Taipei, Taiwan
XP
1,276
Country
United States
So this is a milestone for me using SDL2.

One of the hard parts in SDL2 development for the Switch, is how to load fonts.

After scouring tons of Github source codes, I've finally figured out how to do this. Steps for you to follow:

  1. In your Makefile, at the very bottom, add the following:

    Code:
    # Add this if your font file type is a TrueType Collection
    %.ttc.o :    %.ttc
        @echo $(notdir $<)
        @$(bin2o)
    
    # Add this if your font file type is a TrueType Font
    %.ttf.o :    %.ttf
        @echo $(notdir $<)
        @$(bin2o)

    Essentially, the Makefile would compile all binary data types (those that are not in text format) into the NRO build.

  2. In your topmost header file (.h), add the following in the format given:

    Code:
    #include <[name of font file][replace periods and whitespaces with underscores][file format].h>

    If you want to use Times New Roman, and it's a TTC file, aptly named "times NeW RoNaN.ttc" on Windows, you will add:

    Code:
    #include <times_NeW_RoNaN_ttc.h>

    This includes case-sensitivity, typos, whitespaces and periods.

  3. In your CPP file, to use SDL2's SDL_ttf function call to load fonts, you do this:

    Code:
    TTF_Font* myFont = TTF_OpenFontRW(SDL_RWFromMem((void *) times_NeW_RoNaN_ttc, times_NeW_RoNaN_ttc_size), 1, 36);

    Note there are 2 variables, times_NeW_RoNaN_ttc and times_NeW_RoNaN_ttc_size. [font name]_size is a variable included when generating the [font name] header files.
And there I go, I can now load fonts!

I know there is another way to do this, and that is to load fonts by passing in a file path to said font file.

I don't know how to make that work though, and I was fussing over this for 2 days, until I resorted to use the method given above.


-----------


The next step for me is to learn how to load a text file using the Makefile build method for the Switch.
 
Last edited by delete12345,
  • Like
Reactions: loler55

delete12345

Well-Known Member
OP
Member
Joined
Feb 27, 2010
Messages
695
Trophies
1
Age
32
Location
Taipei, Taiwan
XP
1,276
Country
United States
I'm currently using Visual Studio 2017 to write code for an SDL2 app for Nintendo Switch, and I'm a heavily invested user who loves IntelliSense. Can't live without it. Some people expressed dissatisfaction and annoyance that I'm using Visual Studio on Windows to write Linux code, and I can sort of understand where they are coming from. But this is a need, not a want.

There are times when I wanted to use IntelliSense to find POSIX standard functions. But because I'm on Windows 10, POSIX header files are not compatible with Windows, because Microsoft made a firm decision saying IntelliSense is not supposed to know anything about POSIX standard header files due to incompatibility with Windows. But also, it won't be until when the time comes where IntelliSense integrates with WSL moreso. Here, Microsoft did mention they haven't yet synchronized the include files from the WSL Linux system to Visual Studio 2017, so it may happen in the future. For now, it will be like this.

The best way for me to get IntelliSense to show POSIX standard C headers and functions, is to run WSL, and do this inside WSL Bash:

Code:
cp -r /usr/include/ /mnt/c/posix/include

This will copy the entire POSIX C header files to Windows 10. It's a whopping 25MB, so it will take a while to recursively copy all the files and folders.

Then in my Visual Studio 2017 Project, I added C:\posix\include to the VC++ Directories > Includes Path in my project's Project Properties. IntelliSense will then be able to find the right function for use with the Nintendo Switch, and I won't have to be annoyed at Google searching the various POSIX C functions. I am, of course, not familiar with all POSIX-exclusive C functions, so having to look them up then writing it out is comparably more tedious than letting IntelliSense look it up for me and using TAB key to enter in the needed functions quickly.

----------------------

Also important:

Because when building SDL2 apps for Switch on Windows, due to an issue with libnx installed from the devkitpro-installer, version 2.2.1, it's not able to fetch the latest libnx at this point in time.

So, in all SDL2 Makefiles on Windows, use the following LIBS flags and add/overwrite it in that Makefile:

LIBS := -lSDL2_ttf `sdl2-config --libs` -L/mnt/c/devkitPro/portlibs/switch/lib -L/mnt/c/devkitPro/libnx/lib -L/mnt/c/devkitPro/portlibs/switch/lib -lfreetype -lbz2 -lpng16 -lz -lm -lnx -lz
 
Last edited by delete12345,
  • Like
Reactions: cearp

delete12345

Well-Known Member
OP
Member
Joined
Feb 27, 2010
Messages
695
Trophies
1
Age
32
Location
Taipei, Taiwan
XP
1,276
Country
United States
Thanks to WinterMute, I now know how to actually use romfs in SDL2 build.
  1. The first thing I need is to install a pacman package called switch-tools. This is essentially a collection of tools and packages. One of the tools needed is the romfs packing tool, that packages whatever files you need to be added to the "romfs" as read-only files into your NRO builds. Without out, romfs would not work. It's on pacman right now.
  2. With that out of the way, I then follow the instructions with the provided switch examples when you install devkitPro libnx toolchains. The examples show how to use romfs.
  3. When building the NRO build, I need to make sure that in the build output logs, it says something like: 1>Writing C:/Users/delete12345/SDL2/romfs/myTextFile.txt to RomFS image...
  4. And then I should be able to use romfs as I wanted to.
Since I'm now using romfs, all current Switch emulators will not work at this point in time.

Still many challenges ahead...
 
Last edited by delete12345,
  • Like
Reactions: stark2022 and cearp

XorTroll

Switching between my 2DS and my Switch
Developer
Joined
Dec 28, 2017
Messages
642
Trophies
1
Location
Nowhere
Website
github.com
XP
4,229
Country
Spain
Everytime I try to build my SDL2 app it gives me the following linker error:
aarch64-none-elf-g++.exe: fatal error: C:/devkitPro/libnx/switch.specs: attempt to rename spec 'link' to already defined spec 'old_link'
Other projects work fine, and the libs of my Makefile are the ones mentioned above for Windows. Any help?
 

delete12345

Well-Known Member
OP
Member
Joined
Feb 27, 2010
Messages
695
Trophies
1
Age
32
Location
Taipei, Taiwan
XP
1,276
Country
United States
Everytime I try to build my SDL2 app it gives me the following linker error:
aarch64-none-elf-g++.exe: fatal error: C:/devkitPro/libnx/switch.specs: attempt to rename spec 'link' to already defined spec 'old_link'
Other projects work fine, and the libs of my Makefile are the ones mentioned above for Windows. Any help?
Ask on #switchdev about this. Just be polite, and make sure you follow given instructions.
 

machine69_420

Member
Newcomer
Joined
May 15, 2018
Messages
23
Trophies
0
Age
28
XP
777
Country
Czech Republic
Everytime I try to build my SDL2 app it gives me the following linker error:
aarch64-none-elf-g++.exe: fatal error: C:/devkitPro/libnx/switch.specs: attempt to rename spec 'link' to already defined spec 'old_link'
Other projects work fine, and the libs of my Makefile are the ones mentioned above for Windows. Any help?

Try to delete the "build" folder. I had this specific error too and that helped me.
 

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
    K3Nv2 @ K3Nv2: I'll just pretend like I know what's going on