cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_windowsmessagebox.c (12903B)


      1/*
      2  Simple DirectMedia Layer
      3  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
      4
      5  This software is provided 'as-is', without any express or implied
      6  warranty.  In no event will the authors be held liable for any damages
      7  arising from the use of this software.
      8
      9  Permission is granted to anyone to use this software for any purpose,
     10  including commercial applications, and to alter it and redistribute it
     11  freely, subject to the following restrictions:
     12
     13  1. The origin of this software must not be misrepresented; you must not
     14     claim that you wrote the original software. If you use this software
     15     in a product, an acknowledgment in the product documentation would be
     16     appreciated but is not required.
     17  2. Altered source versions must be plainly marked as such, and must not be
     18     misrepresented as being the original software.
     19  3. This notice may not be removed or altered from any source distribution.
     20*/
     21#include "../../SDL_internal.h"
     22
     23#if SDL_VIDEO_DRIVER_WINDOWS
     24
     25#include "../../core/windows/SDL_windows.h"
     26
     27#include "SDL_assert.h"
     28#include "SDL_windowsvideo.h"
     29
     30
     31#ifndef SS_EDITCONTROL
     32#define SS_EDITCONTROL  0x2000
     33#endif
     34
     35/* Display a Windows message box */
     36
     37#pragma pack(push, 1)
     38
     39typedef struct
     40{
     41    WORD dlgVer;
     42    WORD signature;
     43    DWORD helpID;
     44    DWORD exStyle;
     45    DWORD style;
     46    WORD cDlgItems;
     47    short x;
     48    short y;
     49    short cx;
     50    short cy;
     51} DLGTEMPLATEEX;
     52
     53typedef struct
     54{
     55    DWORD helpID;
     56    DWORD exStyle;
     57    DWORD style;
     58    short x;
     59    short y;
     60    short cx;
     61    short cy;
     62    DWORD id;
     63} DLGITEMTEMPLATEEX;
     64
     65#pragma pack(pop)
     66
     67typedef struct
     68{
     69    DLGTEMPLATEEX* lpDialog;
     70    Uint8 *data;
     71    size_t size;
     72    size_t used;
     73} WIN_DialogData;
     74
     75
     76static INT_PTR MessageBoxDialogProc(HWND hDlg, UINT iMessage, WPARAM wParam, LPARAM lParam)
     77{
     78    switch ( iMessage ) {
     79    case WM_COMMAND:
     80        /* Return the ID of the button that was pushed */
     81        EndDialog(hDlg, LOWORD(wParam));
     82        return TRUE;
     83
     84    default:
     85        break;
     86    }
     87    return FALSE;
     88}
     89
     90static SDL_bool ExpandDialogSpace(WIN_DialogData *dialog, size_t space)
     91{
     92    size_t size = dialog->size;
     93
     94    if (size == 0) {
     95        size = space;
     96    } else {
     97        while ((dialog->used + space) > size) {
     98            size *= 2;
     99        }
    100    }
    101    if (size > dialog->size) {
    102        void *data = SDL_realloc(dialog->data, size);
    103        if (!data) {
    104            SDL_OutOfMemory();
    105            return SDL_FALSE;
    106        }
    107        dialog->data = data;
    108        dialog->size = size;
    109        dialog->lpDialog = (DLGTEMPLATEEX*)dialog->data;
    110    }
    111    return SDL_TRUE;
    112}
    113
    114static SDL_bool AlignDialogData(WIN_DialogData *dialog, size_t size)
    115{
    116    size_t padding = (dialog->used % size);
    117
    118    if (!ExpandDialogSpace(dialog, padding)) {
    119        return SDL_FALSE;
    120    }
    121
    122    dialog->used += padding;
    123
    124    return SDL_TRUE;
    125}
    126
    127static SDL_bool AddDialogData(WIN_DialogData *dialog, const void *data, size_t size)
    128{
    129    if (!ExpandDialogSpace(dialog, size)) {
    130        return SDL_FALSE;
    131    }
    132
    133    SDL_memcpy(dialog->data+dialog->used, data, size);
    134    dialog->used += size;
    135
    136    return SDL_TRUE;
    137}
    138
    139static SDL_bool AddDialogString(WIN_DialogData *dialog, const char *string)
    140{
    141    WCHAR *wstring;
    142    WCHAR *p;
    143    size_t count;
    144    SDL_bool status;
    145
    146    if (!string) {
    147        string = "";
    148    }
    149
    150    wstring = WIN_UTF8ToString(string);
    151    if (!wstring) {
    152        return SDL_FALSE;
    153    }
    154
    155    /* Find out how many characters we have, including null terminator */
    156    count = 0;
    157    for (p = wstring; *p; ++p) {
    158        ++count;
    159    }
    160    ++count;
    161
    162    status = AddDialogData(dialog, wstring, count*sizeof(WCHAR));
    163    SDL_free(wstring);
    164    return status;
    165}
    166
    167static int s_BaseUnitsX;
    168static int s_BaseUnitsY;
    169static void Vec2ToDLU(short *x, short *y)
    170{
    171    SDL_assert(s_BaseUnitsX != 0); /* we init in WIN_ShowMessageBox(), which is the only public function... */
    172
    173    *x = MulDiv(*x, 4, s_BaseUnitsX);
    174    *y = MulDiv(*y, 8, s_BaseUnitsY);
    175}
    176
    177
    178static SDL_bool AddDialogControl(WIN_DialogData *dialog, WORD type, DWORD style, DWORD exStyle, int x, int y, int w, int h, int id, const char *caption)
    179{
    180    DLGITEMTEMPLATEEX item;
    181    WORD marker = 0xFFFF;
    182    WORD extraData = 0;
    183
    184    SDL_zero(item);
    185    item.style = style;
    186    item.exStyle = exStyle;
    187    item.x = x;
    188    item.y = y;
    189    item.cx = w;
    190    item.cy = h;
    191    item.id = id;
    192
    193    Vec2ToDLU(&item.x, &item.y);
    194    Vec2ToDLU(&item.cx, &item.cy);
    195
    196    if (!AlignDialogData(dialog, sizeof(DWORD))) {
    197        return SDL_FALSE;
    198    }
    199    if (!AddDialogData(dialog, &item, sizeof(item))) {
    200        return SDL_FALSE;
    201    }
    202    if (!AddDialogData(dialog, &marker, sizeof(marker))) {
    203        return SDL_FALSE;
    204    }
    205    if (!AddDialogData(dialog, &type, sizeof(type))) {
    206        return SDL_FALSE;
    207    }
    208    if (!AddDialogString(dialog, caption)) {
    209        return SDL_FALSE;
    210    }
    211    if (!AddDialogData(dialog, &extraData, sizeof(extraData))) {
    212        return SDL_FALSE;
    213    }
    214    ++dialog->lpDialog->cDlgItems;
    215
    216    return SDL_TRUE;
    217}
    218
    219static SDL_bool AddDialogStatic(WIN_DialogData *dialog, int x, int y, int w, int h, const char *text)
    220{
    221    DWORD style = WS_VISIBLE | WS_CHILD | SS_LEFT | SS_NOPREFIX | SS_EDITCONTROL;
    222    return AddDialogControl(dialog, 0x0082, style, 0, x, y, w, h, -1, text);
    223}
    224
    225static SDL_bool AddDialogButton(WIN_DialogData *dialog, int x, int y, int w, int h, const char *text, int id, SDL_bool isDefault)
    226{
    227    DWORD style = WS_VISIBLE | WS_CHILD;
    228    if (isDefault) {
    229        style |= BS_DEFPUSHBUTTON;
    230    } else {
    231        style |= BS_PUSHBUTTON;
    232    }
    233    return AddDialogControl(dialog, 0x0080, style, 0, x, y, w, h, id, text);
    234}
    235
    236static void FreeDialogData(WIN_DialogData *dialog)
    237{
    238    SDL_free(dialog->data);
    239    SDL_free(dialog);
    240}
    241
    242static WIN_DialogData *CreateDialogData(int w, int h, const char *caption)
    243{
    244    WIN_DialogData *dialog;
    245    DLGTEMPLATEEX dialogTemplate;
    246    WORD WordToPass;
    247
    248    SDL_zero(dialogTemplate);
    249    dialogTemplate.dlgVer = 1;
    250    dialogTemplate.signature = 0xffff;
    251    dialogTemplate.style = (WS_CAPTION | DS_CENTER | DS_SHELLFONT);
    252    dialogTemplate.x = 0;
    253    dialogTemplate.y = 0;
    254    dialogTemplate.cx = w;
    255    dialogTemplate.cy = h;
    256    Vec2ToDLU(&dialogTemplate.cx, &dialogTemplate.cy);
    257
    258    dialog = (WIN_DialogData *)SDL_calloc(1, sizeof(*dialog));
    259    if (!dialog) {
    260        return NULL;
    261    }
    262
    263    if (!AddDialogData(dialog, &dialogTemplate, sizeof(dialogTemplate))) {
    264        FreeDialogData(dialog);
    265        return NULL;
    266    }
    267
    268    /* No menu */
    269    WordToPass = 0;
    270    if (!AddDialogData(dialog, &WordToPass, 2)) {
    271        FreeDialogData(dialog);
    272        return NULL;
    273    }
    274
    275    /* No custom class */
    276    if (!AddDialogData(dialog, &WordToPass, 2)) {
    277        FreeDialogData(dialog);
    278        return NULL;
    279    }
    280
    281    /* title */
    282    if (!AddDialogString(dialog, caption)) {
    283        FreeDialogData(dialog);
    284        return NULL;
    285    }
    286
    287    /* Font stuff */
    288    {
    289        /*
    290         * We want to use the system messagebox font.
    291         */
    292        BYTE ToPass;
    293
    294        NONCLIENTMETRICSA NCM;
    295        NCM.cbSize = sizeof(NCM);
    296        SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, 0, &NCM, 0);
    297
    298        /* Font size - convert to logical font size for dialog parameter. */
    299        {
    300            HDC ScreenDC = GetDC(0);
    301            WordToPass = (WORD)(-72 * NCM.lfMessageFont.lfHeight / GetDeviceCaps(ScreenDC, LOGPIXELSY));
    302            ReleaseDC(0, ScreenDC);
    303        }
    304
    305        if (!AddDialogData(dialog, &WordToPass, 2)) {
    306            FreeDialogData(dialog);
    307            return NULL;
    308        }
    309
    310        /* Font weight */
    311        WordToPass = (WORD)NCM.lfMessageFont.lfWeight;
    312        if (!AddDialogData(dialog, &WordToPass, 2)) {
    313            FreeDialogData(dialog);
    314            return NULL;
    315        }
    316
    317        /* italic? */
    318        ToPass = NCM.lfMessageFont.lfItalic;
    319        if (!AddDialogData(dialog, &ToPass, 1)) {
    320            FreeDialogData(dialog);
    321            return NULL;
    322        }
    323
    324        /* charset? */
    325        ToPass = NCM.lfMessageFont.lfCharSet;
    326        if (!AddDialogData(dialog, &ToPass, 1)) {
    327            FreeDialogData(dialog);
    328            return NULL;
    329        }
    330
    331        /* font typeface. */
    332        if (!AddDialogString(dialog, NCM.lfMessageFont.lfFaceName)) {
    333            FreeDialogData(dialog);
    334            return NULL;
    335        }
    336    }
    337
    338    return dialog;
    339}
    340
    341int
    342WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
    343{
    344    WIN_DialogData *dialog;
    345    int i, x, y;
    346    UINT_PTR which;
    347    const SDL_MessageBoxButtonData *buttons = messageboxdata->buttons;
    348    HFONT DialogFont;
    349    SIZE Size;
    350    RECT TextSize;
    351    wchar_t* wmessage;
    352    TEXTMETRIC TM;
    353
    354
    355    const int ButtonWidth = 88;
    356    const int ButtonHeight = 26;
    357    const int TextMargin = 16;
    358    const int ButtonMargin = 12;
    359
    360
    361    /* Jan 25th, 2013 - dant@fleetsa.com
    362     *
    363     *
    364     * I've tried to make this more reasonable, but I've run in to a lot
    365     * of nonsense.
    366     *
    367     * The original issue is the code was written in pixels and not
    368     * dialog units (DLUs). All DialogBox functions use DLUs, which
    369     * vary based on the selected font (yay).
    370     *
    371     * According to MSDN, the most reliable way to convert is via
    372     * MapDialogUnits, which requires an HWND, which we don't have
    373     * at time of template creation.
    374     *
    375     * We do however have:
    376     *  The system font (DLU width 8 for me)
    377     *  The font we select for the dialog (DLU width 6 for me)
    378     *
    379     * Based on experimentation, *neither* of these return the value
    380     * actually used. Stepping in to MapDialogUnits(), the conversion
    381     * is fairly clear, and uses 7 for me.
    382     *
    383     * As a result, some of this is hacky to ensure the sizing is
    384     * somewhat correct.
    385     *
    386     * Honestly, a long term solution is to use CreateWindow, not CreateDialog.
    387     *
    388
    389     *
    390     * In order to get text dimensions we need to have a DC with the desired font.
    391     * I'm assuming a dialog box in SDL is rare enough we can to the create.
    392     */
    393    HDC FontDC = CreateCompatibleDC(0);
    394
    395    {
    396        /* Create a duplicate of the font used in system message boxes. */
    397        LOGFONT lf;
    398        NONCLIENTMETRICS NCM;
    399        NCM.cbSize = sizeof(NCM);
    400        SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &NCM, 0);
    401        lf = NCM.lfMessageFont;
    402        DialogFont = CreateFontIndirect(&lf);
    403    }
    404
    405    /* Select the font in to our DC */
    406    SelectObject(FontDC, DialogFont);
    407
    408    {
    409        /* Get the metrics to try and figure our DLU conversion. */
    410        GetTextMetrics(FontDC, &TM);
    411        s_BaseUnitsX = TM.tmAveCharWidth + 1;
    412        s_BaseUnitsY = TM.tmHeight;
    413    }
    414
    415    /* Measure the *pixel* size of the string. */
    416    wmessage = WIN_UTF8ToString(messageboxdata->message);
    417    SDL_zero(TextSize);
    418    Size.cx = DrawText(FontDC, wmessage, -1, &TextSize, DT_CALCRECT);
    419
    420    /* Add some padding for hangs, etc. */
    421    TextSize.right += 2;
    422    TextSize.bottom += 2;
    423
    424    /* Done with the DC, and the string */
    425    DeleteDC(FontDC);
    426    SDL_free(wmessage);
    427
    428    /* Increase the size of the dialog by some border spacing around the text. */
    429    Size.cx = TextSize.right - TextSize.left;
    430    Size.cy = TextSize.bottom - TextSize.top;
    431    Size.cx += TextMargin * 2;
    432    Size.cy += TextMargin * 2;
    433
    434    /* Ensure the size is wide enough for all of the buttons. */
    435    if (Size.cx < messageboxdata->numbuttons * (ButtonWidth + ButtonMargin) + ButtonMargin)
    436        Size.cx = messageboxdata->numbuttons * (ButtonWidth + ButtonMargin) + ButtonMargin;
    437
    438    /* Add vertical space for the buttons and border. */
    439    Size.cy += ButtonHeight + TextMargin;
    440
    441    dialog = CreateDialogData(Size.cx, Size.cy, messageboxdata->title);
    442    if (!dialog) {
    443        return -1;
    444    }
    445
    446    if (!AddDialogStatic(dialog, TextMargin, TextMargin, TextSize.right - TextSize.left, TextSize.bottom - TextSize.top, messageboxdata->message)) {
    447        FreeDialogData(dialog);
    448        return -1;
    449    }
    450
    451    /* Align the buttons to the right/bottom. */
    452    x = Size.cx - ButtonWidth - ButtonMargin;
    453    y = Size.cy - ButtonHeight - ButtonMargin;
    454    for (i = 0; i < messageboxdata->numbuttons; ++i) {
    455        SDL_bool isDefault;
    456
    457        if (buttons[i].flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) {
    458            isDefault = SDL_TRUE;
    459        } else {
    460            isDefault = SDL_FALSE;
    461        }
    462        if (!AddDialogButton(dialog, x, y, ButtonWidth, ButtonHeight, buttons[i].text, i, isDefault)) {
    463            FreeDialogData(dialog);
    464            return -1;
    465        }
    466        x -= ButtonWidth + ButtonMargin;
    467    }
    468
    469    /* FIXME: If we have a parent window, get the Instance and HWND for them */
    470    which = DialogBoxIndirect(NULL, (DLGTEMPLATE*)dialog->lpDialog, NULL, (DLGPROC)MessageBoxDialogProc);
    471    *buttonid = buttons[which].buttonid;
    472
    473    FreeDialogData(dialog);
    474    return 0;
    475}
    476
    477#endif /* SDL_VIDEO_DRIVER_WINDOWS */
    478
    479/* vi: set ts=4 sw=4 expandtab: */